아무도 문서화하지 않은 원격 MCP 클라이언트 설정 매트릭스 (그리고 `type`이 조용히 실패하는 세 가지 방법)
요약
원격 MCP 서버를 관리하며 다양한 AI 클라이언트(Cursor, Claude Code, VS Code 등)의 설정 매트릭스를 분석했습니다. 이 글은 동일한 HTTP 전송 계층이 클라이언트에 따라 다르게 해석되는 문제를 다루며, 특히 잘못된 `type` 값으로 인해 발생하는 세 가지 실패 시나리오를 제시합니다.
핵심 포인트
- 다양한 AI 클라이언트별 MCP 설정 매트릭스를 수집했습니다.
- 클라이언트마다 동일 엔드포인트가 다른 방식으로 JSON을 읽습니다.
- 잘못된 'type' 값이 실패하는 3가지 패턴을 분석하여 공유합니다.
아무도 문서화하지 않은 원격 MCP 클라이언트 설정 매트릭스 (그리고 type이 조용히 실패하는 세 가지 방법)
저는 정적 Authorization: Bearer 헤더로 인증하는 원격 MCP 서버를 유지 관리합니다. OAuth도, 디바이스 플로우도, 브라우저 핸드오프도 없습니다. 이것은 지루한 케이스이며, 모든 클라이언트가 자신만의 의견을 가지고 있는 경우임이 밝혀졌습니다.
Cursor, Windsurf, Claude Desktop, Claude Code, Cline, VS Code, Codex에 동일한 엔드포인트를 연결하고 stdio 전용 호스트를 위한 2줄 브리지를 거치며 한 달 동안 작업하면서 하나의 표를 수집했습니다. 아래에 있으며, 이는 동일한 HTTP 전송 계층이 어떤 클라이언트가 JSON을 읽느냐에 따라 네 가지 다른 이름을 갖기 때문에 존재합니다.
흥미로운 점은 이름 자체가 다르다는 것이 아닙니다. 잘못된 이름이 완전히 다른 세 가지 방식으로 실패한다는 것과, 그중 두 가지는 설정 오류처럼 보이지 않는다는 것입니다.
첫째, HTTP 수준에서 전송 계층을 자격 증명으로부터 분리하기
어떤 클라이언트를 건드리기 전에 하나의 질문에 답하십시오. 엔드포인트가 curl로 작동하는지 여부입니다. 만약 그렇다면, 그 이후의 모든 것은 클라이언트 설정이며, 서버 디버깅을 중단해야 합니다.
export MCP_URL='https://mcp.turingcorp.net/mcp'
# A) 자격 증명 없음으로 발견 (discovery). 설계상 열려 있음. 200이 나오면 = 전송 계층은 정상임.
...
세 가지 결과를 합격/실패가 아닌 의사 결정표로 읽으십시오:
| A (열림) | B (잘못된 값 전달) | C (실제 성공) | 의미하는 바 |
|---|---|---|---|
| 200이 아님 | — | — | 전송 계층 문제: 잘못된 경로, 잘못된 type, 프록시 또는 요청을 소비하는 클라이언트 측 브리지를 사용함 |
| ... |
그리고 알아두면 좋은 함정 하나: tools/list는 자격 증명 테스트가 아닙니다. A 라인을 쓰레기 헤더로 실행하면, 메서드가 열려 있고 헤더가 결코 참조되지 않기 때문에 여전히 전체 도구 목록과 함께 200을 받게 됩니다. 리스팅 호출 형태로 작성된
| Client | Config location | Parent key | Transport field | Value |
|---|---|---|---|---|
| Claude Code | CLI | — | --transport | http |
| ... | ||||
| The 헤더 자체는 어디서든 눈에 띄지 않기 때문에, JSON 형식으로 된 전체 내용은 다음과 같습니다: |
{
"mcpServers": {
"MyServer": {
...
Cline은 하이픈(hyphen) 없이 camelCase로 정확히 한 글자만 다릅니다:
{
"mcpServers": {
"MyServer": {
...
VS Code는 mcpServers가 아닌 servers 아래의 동일한 본문을 사용합니다. Codex는 자격 증명을 TOML 테이블로 유지하여 UI가 이를 알 필요가 없게 만듭니다:
[mcp_servers.MyServer]
url = "https://your-server.example.com/mcp"
http_headers = { Authorization = "Bearer <your pass>" }
...
잘못된 type이 실패하는 세 가지 방법
제가 한 달 전에 읽었으면 좋았을 부분인데, 세 가지 중 오타처럼 보이는 것은 단 하나입니다.
1. 거부됨, 크게. 클라이언트가 transport 필드를 닫힌 집합(closed set)과 비교하여 유효하지 않은 값이라고 알려줍니다. 저렴합니다. 10초 만에 수정할 수 있습니다. 모든 클라이언트는 이 방식이어야 합니다.
2. stdio로 조용히 처리됨. type을 생략하고 빈 url만 남겨두면, 적어도 한 클라이언트 계열은 해당 항목을 로컬 stdio 서버로 읽어 들인 다음, 아무것도 실행하지 않거나 스폰 실패(spawn failure)를 보고합니다. 설정은 올바르게 보이고, 오류 메시지는 존재하지 않는 프로세스를 가리키며, URL은 절대 연결되지 않습니다. 만약 클라이언트가 원격 엔드포인트에 대해 "server exited"라고 말한다면, 다른 어떤 것을 확인하기 전에 type이 존재하는지 먼저 확인하십시오.
3. 연결은 되지만 첫 호출에서 401 에러가 발생합니다. 최악의 경우입니다. 클라이언트가 streamable HTTP 대신 SSE를 협상하고, SSE를 지원하지 않는 서버가 405 응답을 반환하거나 (클라이언트가 작동하는 무언가를 찾을 때까지 폴백(fallback)할 때), 생존성 지표(liveness indicator)는 발견만으로 녹색이 되고, 첫 실제 호출에서 실패합니다. type이 누락되었거나 streamable-http로 오타가 난 경우 Cline은 정확히 이런 방식으로 작동합니다: SSE로 폴백하고, 제 엔드포인트가 SSE를 제공하지 않기 때문에 연결이 서버 결함이라기보다는 한 단어 설정 수정으로 해결될 수 있는 것처럼 보이는 405와 함께 끊어집니다.
세 가지 경우 모두에 걸쳐 공통적인 질문은 이것입니다: type이 존재하는가, 그리고 이 특정 클라이언트가 사용하는 단어를 담고 있는가? 클라이언트 간의 철자법 통일성은 없습니다. 이 단어는 클라이언트의 계약(contract) 일부이며, 프롬프트나 다른 클라이언트에서 복사한 README 파일이 아닌 설정 파일에 포함되어야 합니다.
stdio 브리지는 자체적인 헤더 트랩을 가지고 있습니다
stdio만 사용하는 호스트의 경우 표준 브리지가 작동하지만, 자격 증명(credential)은 --header를 통해 전달되어야 합니다:
{
"mcpServers": {
"MyServer": {
...
각각 저에게 밤을 보내게 한 두 가지 세부 사항이 있습니다. 첫째, mcp-remote는 AUTHORIZATION 환경 변수를 읽지 않으므로, 하나의 설정만 지정하는 구성은 연결하고, 도구를 나열한 다음, 첫 호출에서 401로 실패합니다. 다시 한번 발견/실행 분리(discovery/execution split)가 다른 복장을 하고 나타난 것입니다. 둘째, Authorization: 뒤에 공백이 누락된 것은 의도적입니다: 일부 호스트는 args 내부의 공백을 손상시키기 때문에, 헤더는 환경 변수와 공백 없는 접두사로 조립됩니다.
조용하게 실패하는 두 가지 설정
이 둘 다 클라이언트 측에서 오류 메시지를 생성하지 않습니다.
| 설정 | 잘못된 값의 예시 | 올바른 값 |
|---|---|---|
| call timeout | 약 60초 후에 끊기는 decide 스타일 호출, 이후 "서버 오류" 발생 | 클라이언트 문서에 명시된 단위(예: 초)로 300초 이상 |
| retrieve-after-timeout | 별도로 청구되는 1초짜리 호출 | 동일한 자격 증명으로 job_id를 통해 가져오기, 일반적으로 7일 보존 |
만약 MCP 서버가 분 단위 작업을 수행한다면, 타임아웃을 설정 파일에 명시하고 해당 데이터를 검색할 경로(retrieve path)도 그 옆에 기록해 두어야 합니다. 왜냐하면 실패 모드가 "클라이언트가 기다리다 포기함"이며, 이 경우 단순히 확인하지 않는 한 "서버 오류"와 구별할 수 없기 때문입니다.
블로그 게시물이 아닌 표를 복사하세요
이 내용을 네 단계에 걸쳐 자체 서버에 일반화하십시오.
- 실제로 지원하는 클라이언트의 매트릭스를 채우고, 각 클라이언트가 원하는 정확한 전송 단어(transport word)를 기입합니다.
- 위의 세 가지
curl명령어를 설치 문서의 첫 번째 진단 항목으로 붙여넣어 사용자가 이슈를 열기 전에 전송 방식과 자격 증명을 분리하여 확인할 수 있도록 합니다. - 어떤 메서드가 인증되지 않았는지 명확하게 밝힙니다.
tools/list는 공개로,tools/call은 보호(protected)하는 것이 방어적인 기본값입니다. 하지만 이는 "연결됨"이라는 신호를 무의미하게 만들므로, 이 점을 미리 알려주는 것만으로도 일련의 지원 티켓을 줄일 수 있습니다. - 타임아웃과 ID를 통한 검색 경로를 아무도 읽지 않는 별도의 성능 페이지가 아닌, 설정 파일과 같은 섹션에 배치합니다.
저는 이 매트릭스를 https://mcp.turingcorp.net/mcp의 결정 엔드포인트(decision endpoint)에 적용했습니다. 동일한 표는 정적 Bearer 헤더를 사용하는 모든 원격 MCP 서버에 적용됩니다. 만약 클라이언트가 표에 없다면, 이 curl 블록은 여전히 세 가지 실패 클래스 중 어느 것에 해당하는지 알려주며, 이는 보통 전체 진단이 됩니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기