MCP 호출이 취소됐을 때: 시간초과·연결 종료·실제 작업 결과 구분하기

MCP 화면에 ‘취소됨’이 떴다고 사용자가 취소 버튼을 눌렀거나, 서버의 변경 작업이 되돌려졌다고 단정할 수는 없습니다. 먼저 실제 프로토콜 버전과 전송 방식을 확인하고, 쓰기 작업이었다면 결과를 조회한 뒤 재시도를 판단해야 합니다.

이 글은 MCP 2026-07-28의 일반 요청 취소와 2025-11-25의 차이를 다룹니다. 특정 제품의 오류를 재현한 보고서가 아니라 공식 규격을 바탕으로 한 진단 안내입니다. 확인일은 2026년 10월 10일이며, Tasks 확장으로 생성된 비동기 작업은 여기서 다루는 일반 요청과 구분합니다.

1. 같은 취소 표시라도 원인은 다를 수 있습니다

취소를 조사할 때 화면의 한 줄보다 유용한 것은 사건의 순서입니다. 요청 시작, 제한 시간 도달, 연결 종료, 서버 처리 완료가 언제 일어났는지 나란히 놓아야 합니다. 아래는 실무에서 쓸 수 있는 분류이며, 특정 앱이 이 상태명을 사용한다는 뜻은 아닙니다.

관측한 사실추가로 확인할 것
사용자가 중단 버튼을 누름해당 요청과 연결된 조작 기록인지
설정된 대기 시간이 끝남클라이언트·SDK·프록시 중 어느 제한인지
응답 스트림이 끊김클라이언트 종료인지 중간 연결 문제인지
화면에 취소 문구만 표시됨원본 오류, 요청 식별자, 시각이 있는지

오류 문구의 단어를 원인으로 바꾸지 마세요. 예를 들어 SDK가 시간초과를 처리하며 요청을 중단했다면 사용자의 직접 조작은 없어도 취소 경로를 밟을 수 있습니다. 취소를 일으킨 주체와 실제 작업 결과는 별도의 질문입니다.

2. 2026-07-28에서는 HTTP와 stdio의 취소 신호가 다릅니다

현재 규격의 Streamable HTTP에서는 해당 요청의 SSE 응답 스트림을 닫는 것이 취소 신호입니다. 서버는 이를 취소로 처리해야 하며, 별도의 notifications/cancelled 메시지를 기다리지 않습니다. 반면 stdio에는 요청마다 닫을 스트림이 없으므로 클라이언트가 해당 요청 ID를 담은 취소 알림을 보냅니다. Streamable HTTP 규격 · Cancellation 규격

점검 대상찾아야 할 흔적
2026-07-28 HTTP의 SSE 응답해당 응답 스트림의 종료 시각
2026-07-28 stdionotifications/cancelled와 requestId
2025-11-25 기존 구현해당 구버전의 취소 알림·전송 규칙
Tasks 확장 비동기 작업일반 요청 취소와 별개인 작업 취소 규약

따라서 최신 HTTP 로그에서 취소 알림이 없다는 이유만으로 “취소 요청이 전달되지 않았다”고 결론 내리면 안 됩니다. 반대로 stdio 프로그램에서는 화면만 닫았다는 설명으로 알림 전송 여부를 대신할 수 없습니다. 먼저 어느 전송 방식의 로그를 보고 있는지 고정하세요.

단, 최종 JSON-RPC 응답을 받은 뒤 스트림이 정상적으로 끝나는 경우와 응답을 받기 전에 연결이 끊긴 경우를 구분해야 합니다. 스트림이 닫혔다는 사실만 떼어 보면 정상 완료까지 취소로 오인할 수 있습니다.

3. 구버전 예제를 그대로 복사하지 않기

2025-11-25 문서는 양쪽이 취소 알림을 보낼 수 있는 모델을 설명하고, 클라이언트의 initialize 취소는 금지합니다. task-augmented 요청에는 별도의 tasks/cancel을 요구합니다. 이 규칙을 초기화·세션 구조가 바뀐 최신 구현과 섞지 않아야 합니다. 2025-11-25 Cancellation

현재 TypeScript SDK v2의 지원 안내도 2026-07-28 HTTP의 요청 SSE 종료와 구버전 HTTP의 취소 알림을 구분합니다. 다만 SDK 버전 번호만으로 실제 사용 중인 프로토콜을 추정하지 마세요. v2의 최신 규격 사용에는 명시적인 선택이 필요하다는 안내가 있습니다. TypeScript SDK v2의 프로토콜 지원 안내

아래는 stdio의 가상 취소 알림입니다. 그대로 전송하는 실행 지침이 아닙니다. requestId는 앞서 보낸, 아직 진행 중이라고 판단하는 실제 요청의 ID와 타입까지 맞아야 합니다. 2026-07-28 HTTP 취소용 POST 예제로 사용하지 마세요.

{
  "jsonrpc": "2.0",
  "method": "notifications/cancelled",
  "params": {
    "requestId": "demo-request-7",
    "reason": "Configured timeout reached"
  }
}

4. 대기 종료와 작업 복구를 구분하기

취소 신호가 도착했을 때 서버가 이미 처리를 끝냈을 수 있습니다. 규격은 이런 경합을 고려하고, 알 수 없는 요청이나 이미 끝난 요청 등의 취소 알림을 무시할 수 있도록 합니다. 또한 진행 알림으로 대기 시간을 갱신하더라도 최대 제한 시간을 두도록 권고합니다. 취소의 타이밍과 시간초과 규칙

이로부터 실무적으로 필요한 조치는 결과 확인입니다. 취소는 완료된 외부 변경을 되돌리는 업무 트랜잭션이 아닙니다. 파일 저장이나 예약 생성처럼 상태가 바뀌는 호출이라면, 허용된 조회로 이미 반영됐는지부터 확인하는 편이 안전합니다. 이 부분은 규격의 경합 조건을 업무에 적용한 설계 권고입니다.

조회 결과다음 판단
원하는 결과가 이미 존재함동일 변경을 반복하지 않고 내용 확인
처리 중임이 확인됨제품이 제공하는 상태 조회·취소 절차 사용
변경되지 않았음이 확인됨원인과 권한·중복 방지 조건을 점검한 뒤 재시도 판단
조회도 불가능함결과 미확인으로 보류하고 담당자에게 확인 요청

가상 예로, 예약 생성 요청이 시간초과됐지만 예약 목록에 같은 건이 존재할 수 있습니다. 같은 버튼을 다시 누르면 두 건이 될 위험이 있습니다. 반대로 목록이 비었다고 해서 곧바로 실패라고 단정하기도 어렵습니다. 목록의 지연 반영 가능성과 해당 서비스의 확정 상태 조회 방법까지 확인해야 합니다.

5. 한 번의 재현에서 남길 최소 기록

본인이 관리하는 격리 환경의 무해한 읽기 요청부터 살펴보세요. 운영 데이터 변경, 결제, 권한 변경을 취소 실험의 재료로 쓰지 않습니다. 아래 양식은 점검용이며, 비밀번호·토큰·고객 원문은 기록하지 않습니다.

제품 / 클라이언트 버전: <확인한 값>
서버 / SDK 버전: <확인한 값 또는 모름>
MCP 프로토콜: <실제 요청의 버전>
전송 방식: <HTTP 또는 stdio>
요청 ID: <비밀정보를 제외한 식별자>
시작 시각과 시간대: <값>
설정된 요청 / 최대 제한 시간: <값 또는 모름>
마지막 진행 알림 시각: <값 또는 없음>
취소 신호와 관측 시각: <스트림 종료 또는 알림>
서버 처리 상태: <완료 / 진행 / 실패 / 미확인>
확인한 외부 결과: <조회 근거 또는 미확인>

클라이언트에서 취소가 관측됐다는 사실, 서버가 작업을 멈췄다는 사실, 외부 결과가 원상태라는 사실을 각각 증거로 채우세요. 한 칸을 확인했다고 나머지 칸까지 완료로 표시하지 않는 것이 핵심입니다. 원본 응답에 요청 ID가 없다면 추측해 채우지 말고 없다고 남깁니다.

함께 읽기와 적용 한계

진행 알림과 완료 응답의 차이는 progressToken과 최종 응답 진단, 재시도의 조건은 결과 조회와 중복 실행 방지, 읽기 전용·멱등성 힌트는 annotations 해석에서 이어서 볼 수 있습니다.

이 글은 MCP 2026-07-28과 2025-11-25 공식 문서, TypeScript SDK v2의 프로토콜 지원 문서를 2026년 10월 10일 대조했습니다. 특정 SDK 패치 버전이나 호스팅 서비스에서 취소·복구를 실행 검증한 결과는 아닙니다. 실제 장애에서는 사용 중인 제품·SDK의 정확한 버전과 로그가 추가로 필요합니다.


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

RUDA DIRECTOR에서 더 알아보기

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

계속 읽기