잘못된 이유로 통과되는 체크들
요약
Model Context Protocol(MCP)의 개정판 릴리스 과정에서 발생한 링크 참조 오류와 이를 해결한 워크플로 개선 사례를 다룹니다. 스냅샷 기반의 명세서가 살아있는 초안(draft)을 참조할 때 발생하는 문제점과 이를 방지하기 위한 자동화된 수정 방식을 설명합니다.
핵심 포인트
- 날짜 지정 스냅샷은 모든 참조 링크가 해당 시점에 고정되어야 함
- 링크가 깨지지 않더라도 최신 초안을 가리키면 재현성이 깨짐
- 릴리스 워크플로 내에서 링크 패턴을 다시 쓰는(rewrite) 자동화가 중요함
- MCP의 대규모 변경 사항(stateless 전환 등)에 따른 대응 필요성
Model Context Protocol의 2026-07-28 개정판이 7월 28일에 컷(cut)되었습니다. 저는 제가 의존하려는 모든 명세서(spec)를 읽는 방식대로, 그날 저녁에 공개된 트리(tree)를 읽었습니다. 그리고 발견한 문제는 산문(prose) 속에 있지 않았습니다. 그것은 릴리스를 생성하는 단계(step)에 있었습니다.
해결된 13개의 링크
개정판을 컷(cut)한다는 것은 살아있는 초안 트리(living draft tree)를 날짜가 지정된 트리로 복사하는 것이며, 복사본 내부의 모든 참조(reference) 또한 그 날짜와 함께 지정되어야 합니다. 그것이 날짜가 지정된 스냅샷(snapshot)이 제공하는 약속의 전부입니다. 1년 후에 2026-07-28 트리를 읽더라도 오늘 제공하는 것과 동일한 내용을 제공해야 합니다.
프로모트(promote) 단계에서 명세서 트리를 복사할 때 하나의 링크 패턴을 다시 작성(rewrite)했고, 그 부분은 제대로 작동했습니다. 하지만 다른 두 가지 패턴은 커버되지 않았습니다. 세 개의 링크는 여전히 초안 스키마(draft schema)를 가리키고 있었는데, 여기에는 바로 다음 줄에서 스스로를 모든 프로토콜 메시지와 구조의 진실의 원천(source of truth)이라고 부르는 링크도 포함되어 있었습니다. 아홉 개는 초안 문서(draft docs) 계층을 가리키고 있었으며, 그중 여덟 개는 보안 모범 사례(security best practices) 페이지를 가리키고 있었습니다. 변경 이력(changelog)의 비교(compare) 링크가 13번째였습니다.
그중 단 하나도 깨진 것은 없었습니다. 모든 링크가 해결(resolve)되었지만, 동결된(frozen) 계층이 아닌 살아있는(living) 계층으로 연결되었습니다. 아마도 이것이 프리뷰 체크(preview check)가 이를 잡아내지 못한 이유일 것입니다. 1년 뒤에 2026-07-28로 표시된 링크를 따라가는 독자는 그때까지 초안(draft)이 무엇이 되었든 간에 그것을 받게 될 것이며, 어디에서도 문제를 보고하지 않을 것입니다.
저는 줄 번호와 이를 찾기 위해 실행했던 두 개의 grep 명령어를 포함하여 이슈를 접수했습니다. 그날 바로 종결되었습니다. 수정된 릴리스에는 13개의 교정된 링크가 포함되어 있으며, 이제 프로모트(promote) 단계는 변경 이력 비교(changelog compare)를 새로운 태그(tag)에 고정(pinned)한 상태로 세 가지 복사된 트리 전체에 대해 세 가지 다시 쓰기(rewrites)를 모두 실행합니다. 후자의 절반이 중요한 부분입니다. 파일 수정은 하나의 개정판에만 유효하지만, 워크플로(workflow) 수정은 그 이후의 모든 개정판에 유효합니다.
같은 형태, 하루 뒤, 나의 게이트(gate)에서
해당 개정판(revision)은 대규모의 파괴적 변경(breaking change)을 포함하고 있습니다. 세션(sessions)이 사라졌고, 초기화 핸드셰이크(initialize handshake)가 사라졌으며, 프로토콜은 상태가 없는(stateless) 방식이 되었고, 서버가 반드시 구현해야 하는 새로운 서버 검색 요청(server discovery request)이 추가되었습니다. 제 MCP 서버에 필요했던 두 가지 종속성(dependencies) 모두 컷오프(cut) 후 이틀 이내에 안정화(stable)되었기에, 저는 이를 이전했습니다.
제 사이트에는 서명된 MCP 서버 카드가 실제로 실행 중인 서버를 설명하는지 증명하는 검증 스크립트(verification script)가 있습니다. 이 스크립트가 존재하는 이유는, 과거에 어떤 카드가 서버가 구현하지도 않은 기능(capabilities)을 몇 주 동안 보이지 않게 선언했던 적이 있었기 때문입니다. 일치성(parity)을 증명하는 방식은 라이브 서버를 대상으로 초기화 핸드셰이크(initialize handshake)를 실행하고 그 결과값을 비교하는 것이었습니다.
새로운 개정판에는 초기화(initialize) 과정이 없습니다. 라이브러리는 여전히 이에 응답하는 호환성 경로(compatibility lane)를 유지하고 있습니다.
따라서 게이트(gate)는 계속 통과되었을 것입니다. 게이트는 항상 보고하던 것과 동일하게 깨끗한 수치를 보고했을 것이며, 새로운 경로가 테스트되지 않는 동안 기존의 경로만을 측정하고 있었을 것입니다. 실패도 아니고, 경고도 아니며, 그저 더 이상 그 의미를 담고 있지 않은 숫자였을 뿐입니다. 저는 마이그레이션(migration)이 배포된 후가 아니라 배포 전에 새로운 검색 요청(discovery request)을 중심으로 일치성 블록(parity block)을 다시 작성했으며, 기존 트랜스포트(transport)에는 상응하는 기능이 없었던 두 가지 체크(check)를 추가했습니다.
두 사례의 공통점
두 사례 모두 다른 옷을 입고 있는 동일한 결함입니다. 릴리스(release)에서는 링크가 정상적으로 해결(resolve)되었기에 아무것도 고장 나지 않은 것처럼 보였습니다. 제 게이트에서는 체크가 통과되었기에 아무것도 빨간색(error)으로 표시되지 않았습니다. 두 경우 모두 텍스트와 도구(tooling) 자체는 개별적으로 문제가 없었지만, 그 사이의 무언가가 조용히 진실이 아니게 되어버린 것입니다.
체커(checker)를 유지 관리하는 누구에게나 도움이 될 실질적인 조언을 드리자면, 오직 '고장'이 났을 때만 실패하는 게이트는 자신이 잘못된 것을 측정하기 시작했다는 사실을 알려줄 수 없습니다. 테스트 대상이 형태를 바꿀 때는, 여러분의 체크가 실제로 무엇을 대상으로 말하고 있는지 읽어보십시오. 제 경우에는 코드 한 줄과 프로토콜 버전(protocol version)이었으며, 이를 단순히 실행하는 것만으로는 찾아낼 수 없었을 것입니다.
이런 방식으로 세 개의 명세서(spec)를 읽으며 제가 얻은 교훈은 예상보다 더 좁은 범위였습니다. 성숙한 명세서의 경우, 산문(prose)은 수백 명의 눈을 거쳤으며 리뷰 프로세스(review process)가 제대로 작동합니다. 하지만 그 주변을 둘러싼 출판 메커니즘(publishing machinery)은 훨씬 적은 수의 눈을 거쳤습니다. 바로 그 지점이 새로운 독자가 여전히 기여할 수 있는 부분입니다.
관련 항목
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기