개발자 문서
개발자 문서

급여 보험료 조회 (MCP 도구 2종)

건강보험, 국민연금, 고용보험의 부과고지를 조회해 사업장 단위 보험료 금액을 받는 MCP 도구 두 개의 준비, 요청, 응답, 오류 안내입니다.

이 도구가 하는 일

건강보험, 국민연금, 고용보험의 부과고지를 기관에서 조회해 사업장의 보험료 금액을 한 번에 받는 MCP 도구 두 개입니다.

이 문서에서는 도구가 부르는 기관 조회 하나하나를 leg 이라고 부릅니다.

도구2개(아래 표)
호출 방식tools/call (JSON-RPC 2.0)
엔드포인트POST https://api.xdata.kr/mcp
인증Authorization: Basic
환경 헤더X-MCP-Env-Scope (선택)
최대 대기조회 하나 60초, 호출 전체 90초

이 도구들은 MCP 로 부를 수 있습니다. REST 로 부르는 법은 아직 이 페이지에 없습니다(준비 중입니다).

도구 두 개

도구언제 쓰나연동할 기관 계정채우는 금액 칸
payroll_pack__premium_summary_v1__generate건강보험과 국민연금의 고지 금액이 필요할 때국민건강보험공단 EDI, 국민연금공단 EDIhealth_insurance_won, long_term_care_won, national_pension_won, combined_insurance_premium_won, deduction_rows[].amount_won
payroll_pack__employment_insurance_summary_v1__generate고용보험(실업급여) 금액만 필요할 때근로복지공단employment_insurance_won, deduction_rows[].amount_won

금액의 뜻을 정확히 읽으세요. 모든 금액은 각 기관이 사업장에 고지한 합계이고 근로자별 금액이 아닙니다.

부르기 전에 준비할 것

  1. 기관 계정을 연동합니다

    콘솔 자격증명 등록에서 도구가 쓰는 기관의 계정을 연결합니다(도구마다 연결할 계정이 다르니 위 표를 보세요).

  2. 기관마다 로그인해 세션을 엽니다

    이 도구는 기관에 로그인하지 않습니다. 기관에 세션이 없거나 만료되면 그 기관의 leg 이 실패하고 오류의 error.data.next_action 에 부를 로그인 도구 이름이 옵니다.

  3. MCP 서버를 연결합니다

    MCP 연동 개요의 순서대로 AI 앱이나 MCP 클라이언트에 XBOSS MCP 서버를 연결합니다.

기관별 로그인 도구

기관로그인 도구이 기관을 쓰는 도구
국민건강보험공단 EDInhis_edi__session__loginpayroll_pack__premium_summary_v1__generate
국민연금공단 EDInps_edi__auth__npki_session__loginpayroll_pack__premium_summary_v1__generate
근로복지공단comwel__auth__npki_session__loginpayroll_pack__employment_insurance_summary_v1__generate

요청

도구 이름과 arguments 를 담아 tools/call 을 보냅니다.

payroll_pack__premium_summary_v1__generate

필드타입필수설명예시
notice_year_monthstring필수조회할 고지연월입니다. YYYYMM 형식의 문자열입니다(예: 202608). 건강보험과 국민연금 고지에서 이 달의 고지를 찾습니다.202608
installment_roundinteger필수국민연금 고지 차수입니다. 1 은 1차 고지(ED02), 2 는 2차 고지(ED08)입니다.1
goji_roundinteger선택건강보험 고지차수입니다. 생략하면 고지연월에 맞는 고지가 하나일 때 그 고지를 씁니다(같은 고지를 작성회차만 달리 여러 번 발급했으면 가장 최근 작성분을 씁니다). 그래도 여럿이면 하나로 정해지지 않아 요약 leg 이 건너뛰어집니다.—
nhis_document_idstring선택건강보험 요약에 쓸 고지서 종류입니다. BMBB_030 은 정기 고지(사업장고지내역서 건강), BMBB_050 은 경정 고지(사업장고지내역서 건강/경정)입니다. 생략하면 BMBB_030 입니다.—
nhis_row_keystring선택건강보험 고지 검색 결과에서 고지 한 건을 정하는 행 키입니다. 조건에 맞는 고지가 여럿이라 하나로 정해지지 않을 때만 씁니다.—
nps_row_keystring선택국민연금 고지 검색 결과에서 고지 한 건을 정하는 행 키입니다. 조건에 맞는 고지가 여럿이라 하나로 정해지지 않을 때만 씁니다.—
output_formatsarray선택이 결과를 엑셀(xlsx) 파일로도 받으려면 적습니다. 예: ["xlsx"]. 파일은 artifacts 에 함께 오고, 빼면 데이터만 옵니다. 사용 토큰은 추가로 쓰지 않습니다.—
API_KEY-받지 않음account_link_id 의 다른 철자입니다. 서버가 같은 칸으로 보고 똑같이 처리합니다.—
accountLinkId-받지 않음account_link_id 의 다른 철자입니다. 서버가 같은 칸으로 보고 똑같이 처리합니다.—
account_link_id-받지 않음받지 않는 칸입니다. 보내도 무시됩니다. 연동한 기관 계정은 서버가 leg 마다 그 기관의 연동으로 자동으로 고릅니다(대표 연동, 없으면 활성 연동이 하나일 때 그것). 어느 기관에 활성 연동이 여럿인데 대표가 없으면 고르지 못해 그 기관의 leg 이 거절됩니다.—
api_key-받지 않음account_link_id 의 다른 철자입니다. 서버가 같은 칸으로 보고 똑같이 처리합니다.—
idempotency_keystring선택이 실행의 멱등 키입니다(비어 있지 않은 문자열, 앞뒤 공백은 지우고 128자까지 씁니다). 같은 키와 같은 조회 조건으로 다시 부르면 새 작업을 만들지 않고 이전 작업을 그대로 돌려받아 같은 결과가 옵니다. 같은 키로 조회 조건을 바꾸면 조회를 하나도 시작하지 않고 호출이 오류 번호 -32602(error_code IDEMPOTENCY_KEY_CONFLICT)로 끝나니, 같은 요청을 다시 보내는 것이면 처음 보낸 인자 그대로 보내고 새 요청이면 새 키를 쓰세요. 넣지 않으면 서버가 호출마다 새 키를 만들어 매번 새 작업으로 처리합니다.payroll-premium-2026-08

payroll_pack__employment_insurance_summary_v1__generate

필드타입필수설명예시
billing_yearstring필수고용산재 부과고지의 부과 연도입니다. 네 자리 연도의 문자열입니다(예: 2026).2026
billing_monthstring | integer필수고용산재 부과고지의 부과 월입니다. 1 부터 12 까지의 정수이거나 그 숫자를 담은 문자열입니다(예: 8).8
business_management_nostring선택근로복지공단 사업장관리번호입니다. 생략하면 로그인한 사업장의 번호를 씁니다.—
output_formatsarray선택이 결과를 엑셀(xlsx) 파일로도 받으려면 적습니다. 예: ["xlsx"]. 파일은 artifacts 에 함께 오고, 빼면 데이터만 옵니다. 사용 토큰은 추가로 쓰지 않습니다.—
API_KEY-받지 않음account_link_id 의 다른 철자입니다. 서버가 같은 칸으로 보고 똑같이 처리합니다.—
accountLinkId-받지 않음account_link_id 의 다른 철자입니다. 서버가 같은 칸으로 보고 똑같이 처리합니다.—
account_link_id-받지 않음받지 않는 칸입니다. 보내도 무시됩니다. 연동한 기관 계정은 서버가 leg 마다 그 기관의 연동으로 자동으로 고릅니다(대표 연동, 없으면 활성 연동이 하나일 때 그것). 어느 기관에 활성 연동이 여럿인데 대표가 없으면 고르지 못해 그 기관의 leg 이 거절됩니다.—
api_key-받지 않음account_link_id 의 다른 철자입니다. 서버가 같은 칸으로 보고 똑같이 처리합니다.—
idempotency_keystring선택이 실행의 멱등 키입니다(비어 있지 않은 문자열, 앞뒤 공백은 지우고 128자까지 씁니다). 같은 키와 같은 조회 조건으로 다시 부르면 새 작업을 만들지 않고 이전 작업을 그대로 돌려받아 같은 결과가 옵니다. 같은 키로 조회 조건을 바꾸면 조회를 하나도 시작하지 않고 호출이 오류 번호 -32602(error_code IDEMPOTENCY_KEY_CONFLICT)로 끝나니, 같은 요청을 다시 보내는 것이면 처음 보낸 인자 그대로 보내고 새 요청이면 새 키를 쓰세요. 넣지 않으면 서버가 호출마다 새 키를 만들어 매번 새 작업으로 처리합니다.payroll-employment-2026-08
bash
# 보험료 요약 호출(다른 도구는 name 과 arguments 만 바꿉니다)
# echo -n "<client_id>:<client_secret>" | base64
export MCP_URL="https://api.xdata.kr/mcp"
export MCP_AUTH_B64="<base64_결과_붙여넣기>"
curl -sS -X POST "$MCP_URL" \
  -H "Authorization: Basic $MCP_AUTH_B64" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "payroll_pack__premium_summary_v1__generate",
      "arguments": {
        "notice_year_month": "202608",
        "installment_round": 1,
        "idempotency_key": "payroll-premium-2026-08"
      }
    }
  }'

응답 읽기

호출이 성공하면 result.content[0].text 에 응답 본문이 JSON 문자열로 담겨 옵니다.

공통 칸

필드타입있음설명
pack_run_idstring항상이 실행의 팩 실행 아이디입니다. 링크가 만료되면 MCP 도구 xdata__job__result 에 이 값을 pack_run_id 로 보내 같은 결과를 다시 읽습니다(새 조회를 만들지 않고 사용 토큰도 쓰지 않습니다).
compositeboolean항상항상 true 입니다. 일반 도구 결과와 이 칸으로 구분합니다.
tool_namestring항상호출한 MCP 도구 이름입니다.
providerstring항상팩의 provider 이름입니다.
actionstring항상팩의 action 이름입니다.
idempotency_keystring항상이 실행의 멱등 키입니다. arguments 에 idempotency_key 를 넣었으면 그 값(앞뒤 공백을 지우고 128자까지), 넣지 않았으면 서버가 만든 값입니다.
partialboolean항상보고가 완전하지 않으면 true 입니다. leg 하나라도 성공하지 못했거나 계산을 하지 못했을 때입니다. true 여도 받은 자료는 그대로 쓸 수 있습니다 - 무엇이 비었는지는 legs 와 computed 가 알려 줍니다.
legsarray항상이 도구가 실행한 leg 의 원본 결과입니다. 정의 순서로 옵니다(조회할 행을 고르는 검색 leg 이 먼저, 그 행으로 인자를 만드는 leg 이 뒤에 옵니다).
legs[].leg_indexnumber항상leg 의 순번입니다(0 부터, 정의 순서).
legs[].providerstring항상이 leg 이 부른 기관입니다.
legs[].actionstring항상이 leg 이 실행한 원자 액션입니다.
legs[].label_kostring항상이 leg 을 화면에 보일 한국어 이름입니다.
legs[].statusstring항상leg 의 결과 상태입니다. SUCCEEDED, FAILED, SKIPPED 중 하나입니다. SUCCEEDED 는 그 조회가 끝났다는 뜻이지 찾은 자료가 있다는 뜻이 아닙니다. 건강보험이나 국민연금 검색 leg 이 고지를 하나도 못 찾아도 SUCCEEDED 이고, 그때 결과의 options 는 비어 있으며 error_code 에 이유가 적힙니다. 고용산재 검색 leg 이 성공해도 결과에 고용보험 상세 후보(detail_candidates)가 없으면 상세 leg 은 실행되지 않습니다. SKIPPED 는 앞 leg 의 결과로 인자를 만들어야 하는 leg 이 그 결과를 쓰지 못해 기관에 보내지 않은 것입니다(앞 leg 이 실패했거나, 앞 leg 이 고른 자료가 없거나 하나로 정해지지 않은 경우). SKIPPED leg 은 job_id 가 null 이고 error.data.error_code 가 COMPOSITE_DEPENDENCY_UNAVAILABLE 이며 error.message 는 한국어 안내 문장입니다. failed_count 는 FAILED 와 SKIPPED 를 함께 셉니다.
legs[].job_idstring | null항상이 leg 이 실행한 개별 작업의 아이디입니다. 제출하지 못한 leg 은 null 입니다.
legs[].resultobject | null항상성공한 leg 의 결과 본문입니다 - 그 원자 액션이 돌려주는 값만 담고, 파일은 legs[].artifacts 에 따로 옵니다. 예시에는 이 도구가 읽는 칸 위주로 실었고 실제 응답에는 칸이 더 있습니다. 실패했거나 건너뛴 leg 은 null 입니다.
legs[].artifactsarray | null항상성공한 leg 의 작업이 낸 파일 목록입니다. 파일을 내지 않았으면 빈 목록이고, 실패했거나 건너뛴 leg 은 null 입니다. 파일은 legs[].result 와 섞이지 않고 이 칸에만 있습니다.
legs[].errorobject | null항상실패한 leg 의 오류입니다. 성공했으면 null 입니다.
legs[].error.codenumber조건부오류 번호입니다(JSON-RPC 코드).
legs[].error.messagestring조건부이 leg 의 오류 문장입니다. 프로그램의 분기에는 code 와 data.error_code 를 쓰고, 문장은 사람이 읽는 용도로만 쓰세요.
legs[].error.dataobject | null조건부오류 종류별 부가 정보입니다. 보통 error_code 가 들어 있고 종류에 따라 다른 칸이 더해집니다. 부가 정보가 없는 오류는 이 칸이 없거나 null 입니다. 시간 초과일 때의 칸은 이 페이지의 시간 초과 절 표에 있습니다.
succeeded_countnumber항상성공한 leg 수입니다.
failed_countnumber항상성공하지 못한 leg 수입니다.
computed_errorobject | null항상계산을 하지 못했을 때만 값이 있고(computed 는 null), 그 밖에는 null 입니다.
computed_error.reasonstring조건부계산하지 못한 이유입니다. 값마다 뜻과 결과(보고가 partial 로 오는지, 오류로 끝나는지)는 「시간 초과와 계산 실패」 절의 표에 있습니다.
computed_error.messagestring조건부이유를 설명하는 문장입니다. 마감으로 계산을 건너뛴 봉투(deadline_exhausted)는 한국어 안내 문장이고, 계산에 필요한 leg 자료가 없는 봉투(dependency_missing)는 어느 leg 의 결과가 없는지 알리는 영어 진단 문장입니다. 사람이 읽는 문장이니 프로그램의 분기에는 reason 을 쓰세요.
artifactsarray항상이 실행이 만든 파일 목록입니다. arguments 에 output_formats 로 형식을 청했을 때만 항목이 생기고, 청하지 않았으면 빈 목록입니다. 항목마다 download_url, name, size_bytes, sha256, expires_at 이 있습니다. 칸의 뜻과 내려받기 주소의 유효 시간은 작업 처리 흐름의 결과 조회(result.artifacts)와 같습니다. 링크가 만료되면 pack_run_id 로 xdata__job__result 를 다시 부릅니다.

payroll_pack__premium_summary_v1__generate 의 계산 칸입니다.

필드타입있음설명
computedobject | null항상두 기관의 요약 결과를 모아 계산한 보험료 재료입니다. 건강보험 고지 합계, 장기요양 고지 합계, 국민연금 최종 보험료와 그 세 금액의 합을 채웁니다. 건강보험과 국민연금 요약 leg 이 모두 성공해야 계산하고, 하나라도 없으면 null 이며 이유는 computed_error 에 있습니다(받은 자료는 legs 에 그대로 있습니다).
computed.health_insurance_wonnumber조건부건강보험 고지 합계 금액(원)입니다. 건강보험료 요약 결과의 summary.health_notice_total 그대로이고, 사업장 단위 금액입니다(근로자별 금액이 아닙니다).
computed.long_term_care_wonnumber조건부노인장기요양 고지 합계 금액(원)입니다. 건강보험료 요약 결과의 summary.care_notice_total 그대로이며, health_insurance_won 에 들어 있지 않은 별도 금액입니다.
computed.national_pension_wonnumber조건부국민연금 최종 연금보험료(원)입니다. 국민연금보험료 요약 결과의 summary.final_pension_premium 그대로입니다.
computed.employment_insurance_wonnull조건부이 도구는 고용보험을 조회하지 않아 항상 null 입니다.
computed.combined_insurance_premium_wonnumber조건부건강보험, 장기요양, 국민연금 세 금액의 합(원)입니다. 고용보험은 더하지 않습니다.
computed.total_insurance_deduction_wonnull조건부세 기관을 모두 조회하는 4대보험 공제 재료 도구만 채웁니다. 이 도구는 항상 null 입니다.
computed.completenessstring조건부계산 결과에 붙는 표지입니다. 이 도구는 항상 full_insurance_subset 입니다(고용보험 요약 도구는 employment_insurance_only 입니다). 이 값이 모든 금액 칸이 채워졌다는 보증은 아닙니다 - 채워진 칸은 각 금액 칸이 null 인지로 확인하세요.
computed.missing_fieldsarray조건부이 도구가 채우지 않는 항목의 이름 목록입니다. 도구 스키마에 있는 입력만 보내면 income_tax_won 과 local_income_tax_won 두 이름이 들어 있습니다 - 세액은 이 도구의 입력 스키마에 없어 채워지지 않습니다.
computed.deduction_rowsarray조건부엑셀 표에 쓰는 항목과 금액 행 목록입니다(computed 스칼라 금액에서 값이 있는 칸만). output_formats 로 xlsx 를 청하면 이 행이 파일의 시트가 됩니다. JSON 응답의 스칼라 금액 칸은 그대로 둡니다.
computed.deduction_rows[].itemstring조건부표의 항목 이름입니다(예: 건강보험, 고용보험(실업급여)).
computed.deduction_rows[].amount_wonnumber조건부그 항목의 금액(원)입니다.

payroll_pack__employment_insurance_summary_v1__generate 의 계산 칸입니다.

필드타입있음설명
computedobject | null항상고용산재 부과고지 상세 결과에서 뽑은 보험료 재료입니다. 고용보험 중 실업급여 보험료 합계 하나를 채웁니다. 상세 leg 이 성공해야 계산하고, 아니면 null 이며 이유는 computed_error 에 있습니다(받은 자료는 legs 에 그대로 있습니다).
computed.health_insurance_wonnull조건부이 도구는 건강보험을 조회하지 않아 항상 null 입니다.
computed.long_term_care_wonnull조건부이 도구는 건강보험을 조회하지 않아 항상 null 입니다.
computed.national_pension_wonnull조건부이 도구는 국민연금을 조회하지 않아 항상 null 입니다.
computed.employment_insurance_wonnumber조건부고용보험 중 실업급여 보험료 합계(원)입니다. 고용산재 부과고지 상세 결과의 reconciliation.page_unemployment_total 그대로입니다. 고용안정과 직업능력개발사업 보험료(같은 결과의 reconciliation.page_stability_total)는 더하지 않습니다.
computed.combined_insurance_premium_wonnull조건부건강보험과 국민연금을 함께 조회하는 도구만 채웁니다. 이 도구는 항상 null 입니다.
computed.total_insurance_deduction_wonnull조건부세 기관을 모두 조회하는 4대보험 공제 재료 도구만 채웁니다. 이 도구는 항상 null 입니다.
computed.completenessstring조건부계산 결과에 붙는 표지입니다. 이 도구는 항상 employment_insurance_only 입니다(다른 두 도구는 full_insurance_subset 입니다). 이 도구는 고용보험 금액 하나만 채웁니다.
computed.missing_fieldsarray조건부이 도구가 채우지 않는 항목의 이름 목록입니다. 도구 스키마에 있는 입력만 보내면 income_tax_won 과 local_income_tax_won 두 이름이 들어 있습니다 - 세액은 이 도구의 입력 스키마에 없어 채워지지 않습니다.
computed.deduction_rowsarray조건부엑셀 표에 쓰는 항목과 금액 행 목록입니다(computed 스칼라 금액에서 값이 있는 칸만). output_formats 로 xlsx 를 청하면 이 행이 파일의 시트가 됩니다. JSON 응답의 스칼라 금액 칸은 그대로 둡니다.
computed.deduction_rows[].itemstring조건부표의 항목 이름입니다(예: 건강보험, 고용보험(실업급여)).
computed.deduction_rows[].amount_wonnumber조건부그 항목의 금액(원)입니다.

아래는 payroll_pack__premium_summary_v1__generate 의 4 leg 이 모두 성공했을 때의 응답 본문입니다.

json
{
  "pack_run_id": "prun_01JC8YH8R0E1ABCDEFGHJKMN0P",
  "composite": true,
  "tool_name": "payroll_pack__premium_summary_v1__generate",
  "provider": "payroll_pack",
  "action": "payroll_pack.premium_summary_v1.generate",
  "idempotency_key": "payroll-premium-2026-08",
  "partial": false,
  "legs": [
    {
      "leg_index": 0,
      "provider": "nhis_edi",
      "action": "nhis_edi.hr_payroll.premium_notices.search",
      "label_ko": "건강보험 고지 검색",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN01",
      "result": {
        "...": "nhis_edi.hr_payroll.premium_notices.search 조회가 돌려주는 값입니다"
      },
      "artifacts": [],
      "error": null
    },
    {
      "leg_index": 1,
      "provider": "nps_edi",
      "action": "nps_edi.b1.pension_notices.search",
      "label_ko": "국민연금 고지 검색",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN02",
      "result": {
        "...": "nps_edi.b1.pension_notices.search 조회가 돌려주는 값입니다"
      },
      "artifacts": [],
      "error": null
    },
    {
      "leg_index": 2,
      "provider": "nhis_edi",
      "action": "nhis_edi.hr_payroll.health_premiums.summary",
      "label_ko": "건강보험료 요약",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN03",
      "result": {
        "...": "nhis_edi.hr_payroll.health_premiums.summary 조회가 돌려주는 값입니다"
      },
      "artifacts": [],
      "error": null
    },
    {
      "leg_index": 3,
      "provider": "nps_edi",
      "action": "nps_edi.b1.national_pension_premiums.summary",
      "label_ko": "국민연금보험료 요약",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN04",
      "result": {
        "...": "nps_edi.b1.national_pension_premiums.summary 조회가 돌려주는 값입니다"
      },
      "artifacts": [],
      "error": null
    }
  ],
  "succeeded_count": 4,
  "failed_count": 0,
  "computed": {
    "health_insurance_won": 1243500,
    "long_term_care_won": 163390,
    "national_pension_won": 1890000,
    "employment_insurance_won": null,
    "combined_insurance_premium_won": 3296890,
    "total_insurance_deduction_won": null,
    "completeness": "full_insurance_subset",
    "missing_fields": [
      "income_tax_won",
      "local_income_tax_won"
    ],
    "deduction_rows": [
      {
        "item": "건강보험",
        "amount_won": 1243500
      },
      {
        "item": "노인장기요양",
        "amount_won": 163390
      },
      {
        "item": "국민연금",
        "amount_won": 1890000
      },
      {
        "item": "건강보험·요양·연금 합계",
        "amount_won": 3296890
      }
    ]
  },
  "computed_error": null,
  "artifacts": []
}

payroll_pack__employment_insurance_summary_v1__generate 가 성공했을 때 봉투의 계산 부분입니다. 나머지는 위 예시와 같은 모양입니다.

json
{
  "partial": false,
  "succeeded_count": 2,
  "failed_count": 0,
  "computed": {
    "health_insurance_won": null,
    "long_term_care_won": null,
    "national_pension_won": null,
    "employment_insurance_won": 378000,
    "combined_insurance_premium_won": null,
    "total_insurance_deduction_won": null,
    "completeness": "employment_insurance_only",
    "missing_fields": [
      "income_tax_won",
      "local_income_tax_won"
    ],
    "deduction_rows": [
      {
        "item": "고용보험(실업급여)",
        "amount_won": 378000
      }
    ]
  }
}

일부만 실패했을 때

한 leg 이 실패해도 나머지 결과는 그대로 옵니다.

json
{
  "pack_run_id": "prun_01JC8YH8R0E1ABCDEFGHJKMN0P",
  "composite": true,
  "tool_name": "payroll_pack__premium_summary_v1__generate",
  "provider": "payroll_pack",
  "action": "payroll_pack.premium_summary_v1.generate",
  "idempotency_key": "payroll-premium-2026-08",
  "partial": true,
  "legs": [
    {
      "leg_index": 0,
      "provider": "nhis_edi",
      "action": "nhis_edi.hr_payroll.premium_notices.search",
      "label_ko": "건강보험 고지 검색",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN01",
      "result": {
        "...": "nhis_edi.hr_payroll.premium_notices.search 조회가 돌려주는 값입니다"
      },
      "artifacts": [],
      "error": null
    },
    {
      "leg_index": 1,
      "provider": "nps_edi",
      "action": "nps_edi.b1.pension_notices.search",
      "label_ko": "국민연금 고지 검색",
      "status": "FAILED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN02",
      "result": null,
      "artifacts": null,
      "error": {
        "code": -32004,
        "message": "세션이 만료됐거나 없습니다(오류 AUTH_REQUIRED). 같은 account_link_id 로 'nps_edi__auth__npki_session__login' 을 먼저 불러 세션을 연 뒤 이 도구를 다시 부르세요.",
        "data": {
          "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN02",
          "error_code": "AUTH_REQUIRED",
          "next_action": "nps_edi__auth__npki_session__login",
          "safe_to_retry": true
        }
      }
    },
    {
      "leg_index": 2,
      "provider": "nhis_edi",
      "action": "nhis_edi.hr_payroll.health_premiums.summary",
      "label_ko": "건강보험료 요약",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN03",
      "result": {
        "...": "nhis_edi.hr_payroll.health_premiums.summary 조회가 돌려주는 값입니다"
      },
      "artifacts": [],
      "error": null
    },
    {
      "leg_index": 3,
      "provider": "nps_edi",
      "action": "nps_edi.b1.national_pension_premiums.summary",
      "label_ko": "국민연금보험료 요약",
      "status": "SKIPPED",
      "job_id": null,
      "result": null,
      "artifacts": null,
      "error": {
        "code": -32004,
        "message": "이 작업에 필요한 앞 단계 결과를 쓸 수 없습니다. 앞 단계 조회 결과를 확인한 뒤 다시 시도해 주세요.",
        "data": {
          "error_code": "COMPOSITE_DEPENDENCY_UNAVAILABLE",
          "leg_index": 3,
          "detail": "nps_edi nps_edi.b1.pension_notices.search did not produce a successful result"
        }
      }
    }
  ],
  "succeeded_count": 2,
  "failed_count": 2,
  "computed": null,
  "computed_error": {
    "reason": "dependency_missing",
    "message": "nps_edi nps_edi.b1.national_pension_premiums.summary did not produce a successful result"
  },
  "artifacts": []
}
  1. partial 을 봅니다

    false 면 모든 leg 이 성공한 보고입니다. true 면 아래를 이어서 봅니다.

  2. 성공하지 못한 leg 을 찾습니다

    legs[].status 가 FAILED 또는 SKIPPED 인 leg 을 봅니다.

  3. 세션 문제라면 로그인합니다

    error.data.error_code 가 AUTH_REQUIRED(세션 없음) 또는 PROVIDER_SESSION_EXPIRED(세션 만료)면 error.data.next_action 의 로그인 도구를 부른 뒤 다시 부릅니다.

  4. 건너뛴 leg 은 앞 leg 을 봅니다

    앞 leg 이 FAILED 면 그것부터 해결합니다.

  5. computed 를 확인합니다

    null 이면 필요한 leg 이 하나라도 없는 것입니다. 그때는 받은 leg 의 결과를 직접 쓰세요(예: legs[].result.summary.health_notice_total).

검색 leg 이 고지를 하나도 찾지 못해도 그 leg 은 SUCCEEDED 입니다.

json
{
  "pack_run_id": "prun_01JC8YH8R0E1ABCDEFGHJKMN0P",
  "composite": true,
  "tool_name": "payroll_pack__premium_summary_v1__generate",
  "provider": "payroll_pack",
  "action": "payroll_pack.premium_summary_v1.generate",
  "idempotency_key": "payroll-premium-2026-08",
  "partial": true,
  "legs": [
    {
      "leg_index": 0,
      "provider": "nhis_edi",
      "action": "nhis_edi.hr_payroll.premium_notices.search",
      "label_ko": "건강보험 고지 검색",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN01",
      "result": {
        "success": false,
        "row_count": 0,
        "options": [],
        "error_code": "PREMIUM_NOTICE_SEARCH_EMPTY",
        "implementation_status": "premium_notice_search_empty"
      },
      "artifacts": [],
      "error": null
    },
    {
      "leg_index": 1,
      "provider": "nps_edi",
      "action": "nps_edi.b1.pension_notices.search",
      "label_ko": "국민연금 고지 검색",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN02",
      "result": {
        "...": "nps_edi.b1.pension_notices.search 조회가 돌려주는 값입니다"
      },
      "artifacts": [],
      "error": null
    },
    {
      "leg_index": 2,
      "provider": "nhis_edi",
      "action": "nhis_edi.hr_payroll.health_premiums.summary",
      "label_ko": "건강보험료 요약",
      "status": "SKIPPED",
      "job_id": null,
      "result": null,
      "artifacts": null,
      "error": {
        "code": -32004,
        "message": "이 작업에 필요한 앞 단계 결과를 쓸 수 없습니다. 앞 단계 조회 결과를 확인한 뒤 다시 시도해 주세요.",
        "data": {
          "error_code": "COMPOSITE_DEPENDENCY_UNAVAILABLE",
          "leg_index": 2,
          "detail": "NHIS premium notice search returned no selectable options"
        }
      }
    },
    {
      "leg_index": 3,
      "provider": "nps_edi",
      "action": "nps_edi.b1.national_pension_premiums.summary",
      "label_ko": "국민연금보험료 요약",
      "status": "SUCCEEDED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN04",
      "result": {
        "...": "nps_edi.b1.national_pension_premiums.summary 조회가 돌려주는 값입니다"
      },
      "artifacts": [],
      "error": null
    }
  ],
  "succeeded_count": 3,
  "failed_count": 1,
  "computed": null,
  "computed_error": {
    "reason": "dependency_missing",
    "message": "nhis_edi nhis_edi.hr_payroll.health_premiums.summary did not produce a successful result"
  },
  "artifacts": []
}

모든 leg 이 성공하지 못했을 때

성공한 leg 이 하나도 없으면 보고를 만들지 않고 호출이 오류로 끝납니다.

연동이 없을 때 - 조회를 제출하기 전에 막힙니다.

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "국민건강보험공단 EDI 연동 계정이 없습니다. https://console.xdata.kr/credentials?link=1&priority=nhis_edi 에서 인증서를 연결한 뒤 이 도구를 다시 호출하세요. 연결은 호출할 때마다 새로 확인하므로 따로 다시 시작할 필요가 없습니다.",
    "data": {
      "composite": true,
      "tool_name": "payroll_pack__premium_summary_v1__generate",
      "idempotency_key": "payroll-premium-2026-08",
      "legs": [
        {
          "leg_index": 0,
          "provider": "nhis_edi",
          "action": "nhis_edi.hr_payroll.premium_notices.search",
          "label_ko": "건강보험 고지 검색",
          "status": "FAILED",
          "job_id": null,
          "result": null,
          "artifacts": null,
          "error": {
            "code": -32602,
            "message": "국민건강보험공단 EDI 연동 계정이 없습니다. https://console.xdata.kr/credentials?link=1&priority=nhis_edi 에서 인증서를 연결한 뒤 이 도구를 다시 호출하세요. 연결은 호출할 때마다 새로 확인하므로 따로 다시 시작할 필요가 없습니다.",
            "data": {
              "error_code": "PRIMARY_ACCOUNT_LINK_NOT_FOUND",
              "link_url": "https://console.xdata.kr/credentials?link=1&priority=nhis_edi"
            }
          }
        },
        {
          "leg_index": 1,
          "provider": "nps_edi",
          "action": "nps_edi.b1.pension_notices.search",
          "label_ko": "국민연금 고지 검색",
          "status": "FAILED",
          "job_id": null,
          "result": null,
          "artifacts": null,
          "error": {
            "code": -32602,
            "message": "국민연금공단 EDI 연동 계정이 없습니다. https://console.xdata.kr/credentials?link=1&priority=nps_edi 에서 인증서를 연결한 뒤 이 도구를 다시 호출하세요. 연결은 호출할 때마다 새로 확인하므로 따로 다시 시작할 필요가 없습니다.",
            "data": {
              "error_code": "PRIMARY_ACCOUNT_LINK_NOT_FOUND",
              "link_url": "https://console.xdata.kr/credentials?link=1&priority=nps_edi"
            }
          }
        },
        {
          "leg_index": 2,
          "provider": "nhis_edi",
          "action": "nhis_edi.hr_payroll.health_premiums.summary",
          "label_ko": "건강보험료 요약",
          "status": "SKIPPED",
          "job_id": null,
          "result": null,
          "artifacts": null,
          "error": {
            "code": -32004,
            "message": "이 작업에 필요한 앞 단계 결과를 쓸 수 없습니다. 앞 단계 조회 결과를 확인한 뒤 다시 시도해 주세요.",
            "data": {
              "error_code": "COMPOSITE_DEPENDENCY_UNAVAILABLE",
              "leg_index": 2,
              "detail": "nhis_edi nhis_edi.hr_payroll.premium_notices.search did not produce a successful result"
            }
          }
        },
        {
          "leg_index": 3,
          "provider": "nps_edi",
          "action": "nps_edi.b1.national_pension_premiums.summary",
          "label_ko": "국민연금보험료 요약",
          "status": "SKIPPED",
          "job_id": null,
          "result": null,
          "artifacts": null,
          "error": {
            "code": -32004,
            "message": "이 작업에 필요한 앞 단계 결과를 쓸 수 없습니다. 앞 단계 조회 결과를 확인한 뒤 다시 시도해 주세요.",
            "data": {
              "error_code": "COMPOSITE_DEPENDENCY_UNAVAILABLE",
              "leg_index": 3,
              "detail": "nps_edi nps_edi.b1.pension_notices.search did not produce a successful result"
            }
          }
        }
      ]
    }
  }
}

작업을 시작한 뒤 모두 실패한 경우 - 예를 들어 어느 기관에도 로그인하지 않았을 때입니다.

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "세션이 만료됐거나 없습니다(오류 AUTH_REQUIRED). 같은 account_link_id 로 'nhis_edi__session__login' 을 먼저 불러 세션을 연 뒤 이 도구를 다시 부르세요."
      },
      {
        "type": "text",
        "text": "(아래 JSON 을 문자열로 담은 값)"
      }
    ],
    "isError": true
  }
}

두 번째 블록의 text 를 JSON 으로 풀면 아래와 같습니다.

json
{
  "composite": true,
  "tool_name": "payroll_pack__premium_summary_v1__generate",
  "idempotency_key": "payroll-premium-2026-08",
  "legs": [
    {
      "leg_index": 0,
      "provider": "nhis_edi",
      "action": "nhis_edi.hr_payroll.premium_notices.search",
      "label_ko": "건강보험 고지 검색",
      "status": "FAILED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN01",
      "result": null,
      "artifacts": null,
      "error": {
        "code": -32004,
        "message": "세션이 만료됐거나 없습니다(오류 AUTH_REQUIRED). 같은 account_link_id 로 'nhis_edi__session__login' 을 먼저 불러 세션을 연 뒤 이 도구를 다시 부르세요.",
        "data": {
          "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN01",
          "error_code": "AUTH_REQUIRED",
          "next_action": "nhis_edi__session__login",
          "safe_to_retry": true
        }
      }
    },
    {
      "leg_index": 1,
      "provider": "nps_edi",
      "action": "nps_edi.b1.pension_notices.search",
      "label_ko": "국민연금 고지 검색",
      "status": "FAILED",
      "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN02",
      "result": null,
      "artifacts": null,
      "error": {
        "code": -32004,
        "message": "세션이 만료됐거나 없습니다(오류 AUTH_REQUIRED). 같은 account_link_id 로 'nps_edi__auth__npki_session__login' 을 먼저 불러 세션을 연 뒤 이 도구를 다시 부르세요.",
        "data": {
          "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN02",
          "error_code": "AUTH_REQUIRED",
          "next_action": "nps_edi__auth__npki_session__login",
          "safe_to_retry": true
        }
      }
    },
    {
      "leg_index": 2,
      "provider": "nhis_edi",
      "action": "nhis_edi.hr_payroll.health_premiums.summary",
      "label_ko": "건강보험료 요약",
      "status": "SKIPPED",
      "job_id": null,
      "result": null,
      "artifacts": null,
      "error": {
        "code": -32004,
        "message": "이 작업에 필요한 앞 단계 결과를 쓸 수 없습니다. 앞 단계 조회 결과를 확인한 뒤 다시 시도해 주세요.",
        "data": {
          "error_code": "COMPOSITE_DEPENDENCY_UNAVAILABLE",
          "leg_index": 2,
          "detail": "nhis_edi nhis_edi.hr_payroll.premium_notices.search did not produce a successful result"
        }
      }
    },
    {
      "leg_index": 3,
      "provider": "nps_edi",
      "action": "nps_edi.b1.national_pension_premiums.summary",
      "label_ko": "국민연금보험료 요약",
      "status": "SKIPPED",
      "job_id": null,
      "result": null,
      "artifacts": null,
      "error": {
        "code": -32004,
        "message": "이 작업에 필요한 앞 단계 결과를 쓸 수 없습니다. 앞 단계 조회 결과를 확인한 뒤 다시 시도해 주세요.",
        "data": {
          "error_code": "COMPOSITE_DEPENDENCY_UNAVAILABLE",
          "leg_index": 3,
          "detail": "nps_edi nps_edi.b1.pension_notices.search did not produce a successful result"
        }
      }
    }
  ]
}

시간 초과와 계산 실패

leg 하나는 제출한 뒤 최대 60초까지 기다리고, 호출 전체는 최대 90초 안에 끝납니다.

error.data 의 칸뜻
job_id시간 안에 끝나지 않은 작업의 아이디입니다. 작업은 서버에서 계속 돌 수 있습니다.
timeout_seconds이 조회를 기다린 한도(초)입니다. 보통 60 이고, 호출 전체 마감(90초)이 가까웠으면 남은 시간이라 더 작습니다(소수일 수 있습니다).
safe_to_retry다시 불러도 되는지 알려 줍니다. 이 도구의 조회는 모두 읽기라 true 입니다.
status_url그 작업의 결과를 GET 으로 읽을 수 있는 주소입니다(REST 로 연동한 서비스에서 같은 API 키를 씁니다).
result_tool_call그 작업의 결과를 읽는 MCP 도구 호출입니다(name 과 arguments 를 그대로 부르면 됩니다). 작업을 다시 돌리지 않고 사용 토큰도 쓰지 않으며, 아직 돌고 있으면 잠시 기다렸다가 알려 줍니다.
json
{
  "code": -32003,
  "message": "요청이 60초 안에 끝나지 않았습니다(작업 번호 job_01JC8YH8R0E1ABCDEFGHJKMN01). 서버가 바쁠 수 있습니다. 결과는 xdata__job__result 도구에 이 job_id 를 넣어 읽을 수 있습니다(작업을 다시 돌리지 않고 사용 토큰도 쓰지 않습니다). 같은 API 키로 연동한 서비스는 GET https://api.xdata.kr/v1/jobs/job_01JC8YH8R0E1ABCDEFGHJKMN01 로도 읽을 수 있습니다. 잠시 뒤 다시 시도하거나, 요청 범위를 줄일 수 있으면 줄이세요.",
  "data": {
    "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN01",
    "timeout_seconds": 60,
    "safe_to_retry": true,
    "status_url": "https://api.xdata.kr/v1/jobs/job_01JC8YH8R0E1ABCDEFGHJKMN01",
    "result_tool_call": {
      "name": "xdata__job__result",
      "arguments": {
        "job_id": "job_01JC8YH8R0E1ABCDEFGHJKMN01"
      }
    }
  }
}

호출 전체 마감(90초)에 걸리면 두 모양 중 하나로 옵니다.

error.data 의 칸뜻
error_code항상 COMPOSITE_TIMEOUT 입니다. 이 값으로 호출 전체 마감을 가려냅니다.
timeout_seconds호출 전체 대기 한도(초)입니다.
phase마감이 된 단계의 이름입니다(진단용 - 분기에 쓰지 않습니다).
job_id제출한 작업의 아이디입니다. 제출하지 못했으면 이 칸이 없습니다.
result_tool_call그 작업의 결과를 읽는 MCP 도구 호출입니다(name 과 arguments 를 그대로 부르면 됩니다). 작업을 다시 돌리지 않고 사용 토큰도 쓰지 않습니다. job_id 가 있을 때만 옵니다.
submitted그 조회의 작업을 이미 제출했는지 알려 줍니다. true 면 job_id 가 함께 옵니다.

성공한 leg 이 하나라도 있으면 결과를 합쳐 computed 를 만듭니다.

computed_error.reason결과뜻
deadline_exhausted보고가 오고 partial호출 전체 마감 시간이 다 돼 계산을 건너뛰었습니다. 받은 자료는 legs 에 있고 봉투는 partial 로 옵니다(computed 는 null).
dependency_missing보고가 오고 partial계산에 필요한 leg 자료가 없습니다. 받은 자료는 legs 에 있고 봉투는 partial 로 옵니다.
validation_failed오류로 끝남계산 중 값 검증에서 실패했습니다(우리 쪽 결함). 봉투를 주지 않고 오류로 끝나며 받은 leg 결과는 오류의 data 에 실립니다.
builder_raised오류로 끝남계산 중 예외가 났습니다(우리 쪽 결함). 봉투를 주지 않고 오류로 끝나며 받은 leg 결과는 오류의 data 에 실립니다.

엑셀 파일을 청했는데 금액 표를 만들 수 없을 때

output_formats: ["xlsx"] 를 넣었는데 금액 표(computed.deduction_rows)를 만들 수 없으면 엑셀 파일은 만들어지지 않지만 호출은 오류가 아니라 성공으로 끝납니다.

모든 조회가 끝나도록 원인을 해결한 뒤 새 idempotency_key 로 다시 부르면 파일이 만들어집니다.

조회와 계산이 끝났는데 파일을 만들거나 저장하지 못했을 때는 호출이 오류로 끝납니다.

같은 요청을 다시 보낼 때

arguments 에 idempotency_key 를 문자열로 넣으면 그 값이 이 실행의 멱등 키가 됩니다(앞뒤 공백은 지우고 128자까지 씁니다).

같은 키로 조회 조건(인자)을 바꿔 부르면 조회를 하나도 시작하지 않고 호출이 JSON-RPC error(번호 -32602)로 끝납니다.

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "같은 idempotency_key 로 내용이 다른 요청이 이미 들어왔습니다. 같은 요청을 다시 보내는 것이면 처음 보낸 인자 그대로 보내고, 새 요청이면 idempotency_key 를 새 값으로 바꾸거나 아예 넣지 마세요(넣지 않으면 서버가 만듭니다).",
    "data": {
      "error_code": "IDEMPOTENCY_KEY_CONFLICT",
      "safe_to_retry": false
    }
  }
}

인자는 같은데 서버가 고른 기관 연동이 바뀌어 검색 leg 의 요청 내용이 달라진 경우에는 그 leg 이 제출에서 같은 오류로 실패합니다.

json
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "같은 idempotency_key 로 내용이 다른 요청이 이미 들어왔습니다. 같은 요청을 다시 보내는 것이면 처음 보낸 인자 그대로 보내고, 새 요청이면 idempotency_key 를 새 값으로 바꾸거나 아예 넣지 마세요(넣지 않으면 서버가 만듭니다).",
    "data": {
      "composite": true,
      "tool_name": "payroll_pack__premium_summary_v1__generate",
      "idempotency_key": "payroll-premium-2026-08",
      "legs": [
        {
          "leg_index": 0,
          "provider": "nhis_edi",
          "action": "nhis_edi.hr_payroll.premium_notices.search",
          "label_ko": "건강보험 고지 검색",
          "status": "FAILED",
          "job_id": null,
          "result": null,
          "artifacts": null,
          "error": {
            "code": -32602,
            "message": "같은 idempotency_key 로 내용이 다른 요청이 이미 들어왔습니다. 같은 요청을 다시 보내는 것이면 처음 보낸 인자 그대로 보내고, 새 요청이면 idempotency_key 를 새 값으로 바꾸거나 아예 넣지 마세요(넣지 않으면 서버가 만듭니다).",
            "data": {
              "error_code": "IDEMPOTENCY_KEY_CONFLICT",
              "safe_to_retry": false
            }
          }
        },
        {
          "leg_index": 1,
          "provider": "nps_edi",
          "action": "nps_edi.b1.pension_notices.search",
          "label_ko": "국민연금 고지 검색",
          "status": "FAILED",
          "job_id": null,
          "result": null,
          "artifacts": null,
          "error": {
            "code": -32602,
            "message": "같은 idempotency_key 로 내용이 다른 요청이 이미 들어왔습니다. 같은 요청을 다시 보내는 것이면 처음 보낸 인자 그대로 보내고, 새 요청이면 idempotency_key 를 새 값으로 바꾸거나 아예 넣지 마세요(넣지 않으면 서버가 만듭니다).",
            "data": {
              "error_code": "IDEMPOTENCY_KEY_CONFLICT",
              "safe_to_retry": false
            }
          }
        },
        {
          "leg_index": 2,
          "provider": "nhis_edi",
          "action": "nhis_edi.hr_payroll.health_premiums.summary",
          "label_ko": "건강보험료 요약",
          "status": "SKIPPED",
          "job_id": null,
          "result": null,
          "artifacts": null,
          "error": {
            "code": -32004,
            "message": "이 작업에 필요한 앞 단계 결과를 쓸 수 없습니다. 앞 단계 조회 결과를 확인한 뒤 다시 시도해 주세요.",
            "data": {
              "error_code": "COMPOSITE_DEPENDENCY_UNAVAILABLE",
              "leg_index": 2,
              "detail": "nhis_edi nhis_edi.hr_payroll.premium_notices.search did not produce a successful result"
            }
          }
        },
        {
          "leg_index": 3,
          "provider": "nps_edi",
          "action": "nps_edi.b1.national_pension_premiums.summary",
          "label_ko": "국민연금보험료 요약",
          "status": "SKIPPED",
          "job_id": null,
          "result": null,
          "artifacts": null,
          "error": {
            "code": -32004,
            "message": "이 작업에 필요한 앞 단계 결과를 쓸 수 없습니다. 앞 단계 조회 결과를 확인한 뒤 다시 시도해 주세요.",
            "data": {
              "error_code": "COMPOSITE_DEPENDENCY_UNAVAILABLE",
              "leg_index": 3,
              "detail": "nps_edi nps_edi.b1.pension_notices.search did not produce a successful result"
            }
          }
        }
      ]
    }
  }
}

키를 넣지 않으면 서버가 호출마다 새 키를 만들어 매번 새 작업으로 처리합니다. 응답의 idempotency_key 에는 실제로 쓴 키가 실립니다.

사용 토큰과 진행 표시

사용 토큰을 세는 환경(데모, 그리고 하루 한도가 정해진 요금제의 정식 환경)에서는 이 도구를 한 번 부를 때 새 작업이 만들어지면 사용 토큰을 씁니다.

결과를 코드로 읽기

read-payroll-premium.mjs
javascript
const response = await fetch(process.env.MCP_URL, {
  method: 'POST',
  headers: {
    Authorization: 'Basic ' + Buffer.from(`${process.env.XBOSS_CLIENT_ID}:${process.env.XBOSS_CLIENT_SECRET}`).toString('base64'),
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    jsonrpc: '2.0',
    id: 1,
    method: 'tools/call',
    params: {
      name: 'payroll_pack__premium_summary_v1__generate',
      arguments: {"notice_year_month":"202608","installment_round":1},
    },
  }),
});
const rpc = await response.json();

if (rpc.error) {
  // 요청 단계에서 막혔습니다(연동 없음, 형식 오류, 한도 등). 모든 leg 이 막힌 경우 leg 별 원인은 rpc.error.data.legs 에 있습니다.
  throw new Error(`${rpc.error.code} ${rpc.error.message}`);
}

const { content, isError } = rpc.result;
if (isError) {
  // 작업을 시작한 뒤 모두 실패했습니다. 두 번째 블록의 JSON 에 leg 별 오류(legs)가 담겨 있습니다.
  const detail = content[1] ? JSON.parse(content[1].text) : {};
  throw new Error(content[0].text + ' ' + JSON.stringify(detail.legs ?? []));
}

const report = JSON.parse(content[0].text);
if (report.partial) {
  // 성공하지 못한 leg 이 있습니다. 로그인 도구 이름은 error.data.next_action 에 있습니다(세션이 없거나 만료된 기관).
  const notReady = report.legs
    .filter((leg) => leg.status !== 'SUCCEEDED')
    .map((leg) => ({ action: leg.action, status: leg.status, code: leg.error?.data?.error_code, login: leg.error?.data?.next_action }));
  console.warn('성공하지 못한 leg:', notReady);
}
if (report.computed) {
  console.log(report.computed.combined_insurance_premium_won);
} else {
  // 계산에 필요한 leg 이 없습니다. 받은 leg 의 결과는 report.legs 에 그대로 있습니다.
  console.log(report.computed_error?.reason);
}
// output_formats 로 xlsx 를 청했을 때만 artifacts 에 파일이 있습니다. 링크가 만료되면 pack_run_id 로 xdata__job__result 를 부릅니다.
for (const file of report.artifacts ?? []) {
  console.log(file.name, file.download_url);
}