MCP 자동완성 후보가 안 뜰 때: completion/complete의 ref·context·hasMore 진단

MCP 입력창에서 글자를 넣어도 후보가 뜨지 않는다면, 자동완성 요청이 나갔는지부터 확인한다. 요청 자체가 없는 경우, 서버가 빈 후보를 반환한 경우, 받은 후보를 화면이 버린 경우는 고칠 위치가 다르다. 연결을 다시 맺는 것만으로 이 셋을 구분할 수는 없다.

이 글은 2026년 10월 9일 확인한 MCP 2026-07-28 규격을 바탕으로 completion/complete를 진단한다. 예시는 가상의 문서 서버를 설명하기 위한 합성 데이터다. 실제 서버 호출이나 특정 앱의 자동완성 UI를 시험한 기록은 아니다. 아래 JSON의 문법과 요청·응답 대응 관계는 로컬에서 점검했다.

후보 조회가 어느 단계에서 멈췄는가

공식 Completion 문서에서 자동완성은 프롬프트와 리소스 템플릿의 인자에 넣을 값을 제안하는 기능이다. 서버는 지원 여부를 completions capability로 알린다. 프롬프트나 리소스가 목록에 있다는 사실만으로 자동완성까지 지원한다고 판단하지 않는다. 드롭다운 같은 특정 화면도 규격이 강제하지 않는다.

따라서 입력창이 조용할 때는 먼저 호스트의 지원 범위와 요청 기록을 본다. completion/complete가 전송되지 않았다면 서버의 후보 검색 함수보다 호스트의 기능 지원·입력 이벤트 연결을 먼저 조사한다. 요청이 나갔다면 다음 표처럼 반환 상태로 범위를 좁힌다.

관측한 상태먼저 볼 근거다음 확인
자동완성 요청이 없음호스트 지원 범위, 입력 이벤트와 요청 기록자동완성 기능이 연결됐는지 확인
최상위 error가 있음code·message와 대상 서버미지원 메서드와 잘못된 입력을 구분
values: [] 반환ref, 인자 이름·입력값·선행 선택같은 조건에 맞는 후보가 실제 있는지 확인
응답에는 후보가 있지만 화면은 비어 있음응답 id와 현재 입력 상태UI 필터·오래된 응답 폐기·렌더링 점검
선행 선택을 바꿔도 후보가 같음context.arguments와 캐시 키이전 조건이 남았는지 확인
hasMore: true인데 다음 페이지를 못 받음요청 스키마와 서버의 별도 확장 여부목록용 cursor를 임의로 붙이지 않음

표의 UI·캐시 점검은 이 글의 구현 진단 권고다. 모든 호스트가 같은 캐시나 필터를 사용한다는 뜻은 아니다. 서버의 오류 응답과 로컬 UI 상태를 한 줄의 “자동완성 실패”로 합치지 않는 데 목적이 있다.

ref는 대상을, argument는 입력 중인 칸을 가리킨다

CompleteRequestParams 스키마에는 두 가지 참조가 있다. 프롬프트는 ref/prompt와 name, 리소스는 ref/resource와 uri를 쓴다. argument.name은 지금 완성하려는 인자 이름이며 argument.value는 현재 입력 문자열이다. 도구 호출의 arguments 객체와 혼동하지 않는다.

다음 가상 서버는 docs://{locale}/{slug}라는 URI 템플릿을 제공한다고 가정한다. 사용자가 언어를 ko로 고른 뒤 문서 이름 칸에 on을 입력했다. 완성할 대상은 slug이고, 이미 정해진 locale은 context.arguments에 넣는다. 실제 작업에서는 resources/templates/list에 나온 템플릿과 변수 이름을 그대로 확인해야 한다.

{
  "jsonrpc": "2.0",
  "id": 81,
  "method": "completion/complete",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {
        "name": "completion-diagnostic-example",
        "version": "1.0.0"
      },
      "io.modelcontextprotocol/clientCapabilities": {}
    },
    "ref": {
      "type": "ref/resource",
      "uri": "docs://{locale}/{slug}"
    },
    "argument": {
      "name": "slug",
      "value": "on"
    },
    "context": {
      "arguments": {
        "locale": "ko"
      }
    }
  }
}

이 JSON은 요청 본문이다. 위 가상 템플릿을 임의의 실제 서버에 보내는 실행 명령이 아니다. HTTP 전송에는 Content-Type: application/json, Accept: application/json, text/event-stream, MCP-Protocol-Version: 2026-07-28, Mcp-Method: completion/complete와 필요한 인증이 별도로 필요하다. 현재 규격의 Mcp-Name 필수 대상에는 completion/complete가 포함되지 않는다. 이 요청의 ref.uri를 resources/read의 헤더 규칙에 그대로 끼워 넣지 않는다.

요청의 _meta에서 protocolVersion과 clientCapabilities는 필수이고 clientInfo는 포함이 권고된다. 요청 메타데이터 규칙을 적용한 예시다. 여기의 빈 clientCapabilities는 이 요청에서 사용할 선택적 클라이언트 기능을 선언하지 않은 상태이며, 서버가 자동완성을 제공한다는 completions 선언과 방향이 다르다. 오래된 초기화 기반 구현에는 해당 버전의 요청 형식을 확인하고 적용한다.

프롬프트 인자의 후보를 조회할 때 바뀌는 부분도 분명히 해 두자. 아래는 가상의 draft_note 프롬프트에서 audience를 완성하는 params의 일부다. 전체 요청으로 쓰려면 앞 예시의 메서드·메타데이터·새 요청 ID를 갖춰야 한다.

{
  "ref": {
    "type": "ref/prompt",
    "name": "draft_note"
  },
  "argument": {
    "name": "audience",
    "value": "일"
  },
  "context": {
    "arguments": {
      "language": "ko"
    }
  }
}

선행 선택을 바꿨다면 같은 입력도 다른 질문이다

언어를 ko에서 en으로 바꾼 뒤에도 slug에 입력한 글자는 on일 수 있다. 이때 후보 조회의 조건은 달라졌다. context.arguments.locale이 이전 값으로 남으면 서버는 이전 언어에 맞는 후보를 돌려줄 수 있다. 선행 인자는 화면의 현재 선택값에서 구성하고, 아직 결정하지 않은 값을 정해진 사실처럼 보내지 않는다.

반대로 context를 전송했다고 항상 후보가 달라지는 것도 아니다. 어떤 인자에 의존할지는 서버 구현이 정한다. 서버가 locale을 실제 검색 조건으로 사용하는지 확인해야 한다. 프로토콜은 문자열 맵을 전달할 자리를 제공하며, 서비스의 후보 생성 규칙까지 만들어 주지는 않는다.

캐시를 둔 구현이라면 입력 접두사만 키로 쓰지 않는지 점검한다. 이 글에서는 서버·참조 대상·인자 이름·현재 값·선행 선택·권한 범위를 함께 구분하도록 권한다. 계정이나 조직이 바뀌었을 때 이전 사용자에게만 허용된 후보를 재사용하지 않아야 한다. 공개 진단 로그에는 토큰이나 실제 비공개 문서 이름 대신 마스킹한 조건을 남긴다.

hasMore는 다음 페이지의 주소가 아니다

가상의 문서 서버가 앞의 요청에 두 후보를 돌려주는 응답은 다음과 같다. CompleteResult 스키마의 values는 문자열 배열이며 한 응답에 최대 100개다. total과 hasMore는 선택 필드다. 필드가 없다는 이유만으로 총 후보가 0개라고 표시하거나 전체를 다 받았다고 단정하지 않는다.

{
  "jsonrpc": "2.0",
  "id": 81,
  "result": {
    "resultType": "complete",
    "completion": {
      "values": [
        "onboarding",
        "onboarding-checklist"
      ],
      "total": 2,
      "hasMore": false
    }
  }
}

hasMore: true는 현재 응답 바깥에 후보가 더 있다는 표시다. 기본 completion/complete 요청에는 cursor가 없고 응답에도 nextCursor가 정의돼 있지 않다. 그러므로 tools/list 페이지네이션 코드를 그대로 복사해 붙이면 안 된다. 표준 스키마만으로 다음 묶음을 요청하는 방법이 생기지는 않는다.

이 경우 UI에서는 “후보가 더 있습니다. 입력을 더 구체적으로 적어 주세요”처럼 안내할 수 있다. 서버가 문서화한 별도 확장이 있다면 그 범위에서 처리한다. 같은 짧은 입력을 무제한 반복 호출해서 전체 후보를 얻으려 하지 않는다. 퍼지 매칭을 사용하는 서버도 있으므로 모든 후보가 입력 문자열로 시작해야 한다는 필터 역시 무조건 적용하지 않는다.

후보를 받은 뒤에도 프롬프트 조회나 리소스 읽기는 별도 단계다. 이 예시에서 onboarding을 선택했다면 템플릿의 다른 변수와 함께 URI를 구성하고, 필요한 권한으로 resources/read를 호출해야 본문을 읽는다. 후보를 제안받았다는 사실만으로 문서 내용이 조회됐거나 최종 접근 권한이 보장됐다고 판단하지 않는다.

늦게 도착한 응답이 현재 후보를 덮지 않게 한다

사용자가 빠르게 입력하면 요청한 순서와 도착한 순서가 달라질 수 있다. 다음은 실제 로그가 아닌 응답 역전을 설명하는 가상 시간선이다. 첫 요청이 늦게 도착했다는 이유만으로 서버 오류라고 결론 내릴 수는 없다.

요청 81: locale=ko, slug="o" 전송
요청 82: locale=ko, slug="on" 전송
응답 82: "on" 기준 후보 도착 → 현재 입력과 일치
응답 81: "o" 기준 후보 도착 → 현재 입력과 불일치
UI 권고: 응답 81로 현재 "on" 후보를 덮지 않음

JSON-RPC 응답의 id는 어떤 요청에 대한 응답인지 대응시킨다. 어느 응답을 현재 입력창에 표시할지는 클라이언트가 결정해야 한다. 요청 ID뿐 아니라 그 요청을 만들 당시의 입력과 선행 선택을 함께 보관해 현재 상태와 비교하는 방법이 유용하다. 입력을 지운 경우에도 예전 후보가 다시 나타나지 않는지 확인한다.

요청 횟수는 입력 디바운스로 줄일 수 있다. 다만 디바운스만으로 이미 전송된 요청의 응답 역전까지 해결되지는 않는다. 서버의 제한에 맞춰 호출 빈도를 조절하고, 미지원 기능이나 접근 거절을 빈 결과로 바꿔 숨기지 않는다. Completion 오류 처리 권고는 미지원 메서드에 -32601, 잘못된 프롬프트 이름이나 필수 인자 문제에 -32602, 내부 오류에 -32603을 구분한다. 실제 오류의 message와 서버 버전도 함께 본다.

다시 확인할 때 남길 최소 기록

확인 시각 / 시간대:
호스트·서버·SDK 버전 / MCP revision:
서버 completions 지원 여부 / 호스트 UI 지원 여부:
ref.type / 이름 또는 마스킹한 URI 템플릿:
argument.name / 입력 조건 / context의 키와 조건:
요청 id / 오류 code 또는 values 개수:
hasMore·total: 값 또는 미제공:
응답 당시 현재 입력과 일치했는가:
선행 선택 변경 / 입력 지우기 / 권한 변경 후 결과:
미검증 범위:

수정 후에는 후보 한 번이 뜨는지만 보지 않는다. 선행 선택을 바꿨을 때, 입력을 지웠을 때, 응답 순서가 뒤집혔을 때, 후보가 없거나 일부만 돌아왔을 때를 나눠 확인한다. 접근이 제한된 후보가 다른 권한 조건에 노출되지 않는지도 중요하다. 공식 보안 요구사항은 입력 검증·호출 제한·민감한 후보 접근 통제와 정보 유출 방지를 요구한다.

이 글에서 검증한 범위는 공개 규격의 메시지 계약과 합성 JSON의 내부 일관성이다. SDK 설치·실행, 인증된 MCP 통신, 호스트 UI, 네트워크 지연 재현은 수행하지 않았다. 캐시 키와 응답 폐기 방법은 구현 권고이며 규격이 지정한 유일한 방식이 아니다.

자동완성 진단의 마지막 확인은 후보의 존재보다 대응 관계에 있다. 지금 선택한 언어와 지금 입력한 문자열에 대해 받은 후보인지 확인해야, 사용자는 엉뚱한 문서를 고르는 다음 오류까지 피할 수 있다.


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

RUDA DIRECTOR에서 더 알아보기

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

계속 읽기