MCP 도구 목록이 일부만 보일 때: nextCursor 페이지네이션 확인

MCP 도구가 일부만 보일 때는 첫 tools/list 응답의 nextCursor와, 그 값을 그대로 보낸 다음 요청이 있는지 확인한다. 첫 페이지를 전체 목록으로 취급하면 뒤쪽 도구가 빠질 수 있다. 특히 빈 문자열도 유효한 커서다. if (nextCursor)처럼 값이 참인지로 반복 여부를 결정하는 코드는 이 경우를 놓친다.

적용 대상은 MCP 클라이언트·연결 어댑터·목록 수집 코드를 점검할 수 있는 개발자다. 일반 사용자는 읽을 수 있는 진단 기록과 호스트의 도구 목록을 비교하는 데 활용할 수 있다. 2026년 10월 8일 한국시간에 최신 공식 규격인 2026-07-28을 확인했다. 이 글은 명세 기반 점검 가이드다. 특정 클라이언트에서 누락을 재현하거나 수정한 사례 보고는 아니다.

첫 페이지의 개수로 전체 개수를 짐작하지 않는다

공식 Pagination 문서는 페이지 크기를 서버가 정하도록 한다. 20개를 받았다는 사실만으로 다음 페이지의 존재를 판정할 수 없다. 서버가 보내는 nextCursor는 내부 구조를 해석하지 않는 문자열이다. 숫자처럼 보여도 1을 더하지 않고, Base64처럼 보여도 해독해 다시 만들지 않는다. 클라이언트는 반환값을 다음 요청의 params.cursor로 전달한다.

nextCursor가 생략되면 종료한다. 값이 문자열이면 길이가 0이어도 이어서 요청한다. null이나 숫자가 왔다면 먼저 해당 버전 스키마 검증 결과를 확인한다. 최신 ListToolsResult는 nextCursor를 선택적 문자열로 정의한다. 비정상 값을 조용히 “목록 끝”으로 처리하면 서버의 형식 오류까지 도구 누락으로 숨길 수 있다.

네 지점의 개수를 나누어 기록한다

점검 지점확인할 내용다음 판단
원시 응답현재 페이지 도구 수, nextCursor 존재·타입후속 페이지가 필요한지 판단
후속 요청이전 커서와 params.cursor의 일치요청이 없으면 클라이언트 반복 로직 확인
누적 목록모든 페이지의 도구 이름과 중복 수페이지를 덮어쓰는지, 합치는지 확인
호스트 화면누적 목록과 실제 노출 목록의 차이필터·선택 설정·권한 차이로 범위 이동

실제 커서 값이나 인증 헤더를 공개 로그에 복사할 필요는 없다. “필드 있음, 문자열, 길이 0, 다음 요청 있음”처럼 구조적 사실로도 첫 분기를 확인할 수 있다. 도구 이름이 내부 업무를 드러내면 개수와 중복 여부만 남긴다. 각 페이지의 응답 ID가 대응 요청 ID와 맞는지도 함께 확인한다.

2026-07-28의 빈 커서 왕복 예제

아래는 가상의 두 페이지를 구성한 JSON이다. 실제 서버 주소나 인증 정보가 없으며 네트워크 요청 명령도 아니다. 첫 요청은 id를 page-1로 두고, 아래 두 번째 요청과 같은 _meta를 넣되 cursor만 생략했다고 가정한다. 첫 응답에는 demo_alpha와 빈 문자열 커서가 온다.

{"jsonrpc":"2.0","id":"page-1",
  "result": {"resultType":"complete","tools":[{"name":"demo_alpha","inputSchema":{"type":"object","additionalProperties":false}}],"nextCursor":"","ttlMs":0,"cacheScope":"private","_meta":{"io.modelcontextprotocol/serverInfo":{"name":"synthetic-server","version":"1.0.0"}}}
}

다음 요청에서 빈 문자열을 제거하지 않는다. 매 요청에 protocolVersion과 clientCapabilities를 넣고, 권장 필드인 clientInfo도 포함했다. 공식 소개 페이지의 짧은 예제는 _meta를 생략하기도 하므로, 복사할 때는 필수 요청 메타데이터 정의까지 확인해야 한다.

{
  "jsonrpc": "2.0",
  "id": "page-2",
  "method": "tools/list",
  "params": {
    "cursor": "",
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {
        "name": "synthetic-client",
        "version": "1.0.0"
      },
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}

두 번째 응답에 demo_beta가 있고 nextCursor가 없다면 이 합성 예제의 수집은 끝이다. 첫 페이지와 합쳐 도구 이름은 두 개가 된다.

{"jsonrpc":"2.0","id":"page-2",
  "result": {"resultType":"complete","tools":[{"name":"demo_beta","inputSchema":{"type":"object","additionalProperties":false}}],"ttlMs":0,"cacheScope":"private","_meta":{"io.modelcontextprotocol/serverInfo":{"name":"synthetic-server","version":"1.0.0"}}}
}

resultType: complete는 해당 요청의 완료를 가리킨다. 그 페이지가 전체 목록의 마지막이라는 뜻으로 읽지 않는다. ttlMs와 cacheScope도 최신 ListToolsResult 스키마에 맞춰 넣었다. stdio로 전송할 때는 위의 보기용 JSON을 메시지별 한 줄로 직렬화해야 한다.

반복 조건보다 중요한 중단 사유

다음은 네트워크 호출 없는 JavaScript 판정 예제다. 커서가 없을 때와 빈 문자열일 때를 구분한다. SDK가 이미 페이지를 자동 순회한다면 바깥에서 같은 루프를 또 붙이지 말고 그 SDK의 실제 반환 동작부터 확인한다.

function cursorState(result) {
  if (!("nextCursor" in result)) return { done: true };
  if (typeof result.nextCursor !== "string") {
    throw new Error("Invalid nextCursor type");
  }
  return { done: false, cursor: result.nextCursor };
}
console.assert(cursorState({}).done === true);
console.assert(cursorState({ nextCursor: "" }).done === false);

실제 수집기에는 반복 커서 감지와 페이지·시간 상한을 두는 편이 안전하다. 이는 이 글의 구현상 권고이며 MCP가 정한 고정 페이지 수는 아니다. 상한에 걸리거나 같은 커서가 되돌아오면 무한 요청을 멈추고 “수집 미완료”로 기록한다. 그때까지 모은 도구만 전체 목록이라고 표시하지 않는다. 잘못된 커서에 대한 -32602가 오면 커서를 추측해 고치지 않는다.

모두 읽어도 목록이 다르면

목록을 읽는 도중 서버의 도구 구성이 바뀔 수 있다. Caching 규칙은 페이지 사이의 일관된 스냅샷을 보장하지 않으며, 일관된 전체 목록이 필요하면 커서 없이 처음부터 다시 수집하도록 권고한다. 유효했던 커서가 거부된 경우에도 페이지 캐시를 버리고 처음부터 다시 읽는 경로를 확인한다. 캐시 키에서 cursor를 빠뜨려 매번 첫 페이지를 반환하는지도 살펴볼 지점이다.

또한 Tools 규칙상 도구 집합은 요청의 권한에 따라 달라질 수 있다. 관리자 화면의 개수와 제한된 사용자에게 반환된 개수를 그대로 비교하면 페이지네이션 문제로 오인하기 쉽다. 같은 권한 조건과 가능한 한 같은 시점의 결과를 비교한다. 이를 확인하려고 권한을 넓히거나 인증을 끄지는 않는다.

원시 목록은 완성됐는데 화면에만 빠진다면 Cursor MCP Connected 이후의 도구 노출 점검으로 넘어간다. 구버전과 최신 요청 구조가 섞였다면 MCP 2026-07-28의 initialize·세션 변경을 먼저 맞춘다. 확인 완료의 기준은 “첫 응답을 받음”보다 구체적이어야 한다. 마지막 페이지에 도달했는지, 중단이 있었다면 무엇 때문인지를 남긴다.

출처 확인일: 2026-10-08 KST. 기준 버전: MCP 2026-07-28. 모든 도구명·클라이언트명·응답 ID는 설명용 합성 값이다.


다른 글 보기 · 주제 탐색 · 작성자와 편집 기준 · 문의·정정 요청

RUDA DIRECTOR에서 더 알아보기

지금 구독하여 계속 읽고 전체 아카이브에 액세스하세요.

계속 읽기