MCP 는 어떻게 표준이 되었나
앞 글에서 MCP 서버는 지능 없는 어댑터일 뿐이라고 썼다. 그렇게 정리하고 나면 새로운 의문이 생긴다. 어떤 서비스의 API 를 감싸는 멍청한 어댑터를 만드는 방법은 수없이 많다. 그런데 왜 하필 MCP 라는 한 가지 규약이 LLM 과 도구를 잇는 표준의 자리를 차지했나. 답은 화려한 기능이 아니라 프로토콜을 어떻게 짰는가에 있다.
새 포맷을 만들지 않았다
가장 먼저 눈에 띄는 것은 MCP 가 새 통신 포맷을 발명하지 않았다는 점이다. 이미 검증된 JSON-RPC 2.0 위에 얹었다.
오가는 메시지는 세 종류뿐이다. 응답을 기대하는 요청(request), 그 요청에 짝지어 돌아오는 응답(response), 그리고 응답이 없는 단방향 통지(notification). 이 메시지를 실어 나르는 전송 계층은 그 아래에 따로 있다. 로컬이면 stdio, 원격이면 HTTP. 위층의 문법은 전송 방식이 무엇이든 동일하다.
새 포맷을 만들지 않은 것은 게으름이 아니라 전략이다. 어느 언어로든 JSON-RPC 를 다루는 코드는 이미 흔하다. 진입 장벽을 일부러 바닥까지 낮춘 선택이다.
셋으로 나뉜 구조
연결의 양 끝은 세 역할로 나뉜다.
- Host: LLM 을 품은 앱. 판단은 여기서 일어난다.
- Client: Host 안에서 서버 하나와 1:1 로 연결을 유지하는 부분. 서버가 여럿이면 클라이언트도 그만큼이다.
- Server: 도구를 노출하는 쪽. 앞 글에서 말한 멍청한 어댑터가 이것이다.
앞 글의 “LLM 은 서버가 아니라 부르는 쪽에 있다” 는 문장이 이 구조에 대응한다. 지능은 Host 에 있고, Server 는 그 지시를 실행할 뿐이다.
서버는 자기를 설명한다
서버가 노출하는 것은 한 종류가 아니다. 모델이 호출하는 도구(tool), 앱이 읽어 가는 자원(resource), 사용자가 고르는 프롬프트(prompt). 통제의 주체로 갈라 둔 설계다. 그중 핵심은 도구이고, 도구의 핵심은 자기 설명이다.
서버에 도구 목록을 물으면, 각 도구의 이름과 설명과 입력 형식을 JSON Schema 로 돌려준다.
{
"name": "create_document",
"description": "Create a new document",
"inputSchema": {
"type": "object",
"properties": { "title": { "type": "string" }, "text": { "type": "string" } },
"required": ["title"]
}
}이 한 조각이 두 가지를 동시에 말한다. 첫째, 사용법 매뉴얼을 따로 쓰지 않아도 되는 이유가 여기 있다. 스키마 자체가 호출법이고, 모델은 그것을 읽어 인자를 채운다. 둘째, 스키마에 선언된 것은 제목과 본문 같은 업무 데이터뿐이다. 자격증명이 낄 자리가 없다. 앞 글에서 시크릿을 도구 인자에 싣지 않는다고 했는데, 그 원칙이 프로토콜의 모양으로 박혀 있는 셈이다.
시작은 악수다
연결이 열리면 곧장 일을 시키지 않는다. 먼저 악수를 한다.
클라이언트와 서버는 initialize 를 주고받으며 서로의 프로토콜 버전과, 자기가 지원하는 기능(capability)을 교환한다. 누가 도구를 갖고 있고 누가 자원을 읽을 수 있는지, 시작 전에 합의한다. 이 협상이 확장성의 근거다. 서로 무엇을 지원하는지 먼저 맞춰 두기 때문에, 새 기능이 더해져도 옛 연결이 깨지지 않는다.
요청과 응답을 한 번 주고받고 끝나는 REST 와 달리, MCP 의 연결은 세션으로 살아 있다. 그래서 서버는 도구 목록이 바뀌었다는 통지를 먼저 보낼 수도 있다. 한쪽이 묻기를 기다리지 않고 상태 변화를 밀어 주는 것이다.
M×N 을 M+N 으로 접다
여기까지가 구조다. 그리고 이 구조가 표준을 만든 진짜 이유로 이어진다.
MCP 이전에는 도구를 AI 앱에 연결하는 일이 매번 맞춤 공사였다. 앱이 M 개, 도구가 N 개라면 둘을 잇는 통합은 M 곱하기 N 개가 필요했다. 어떤 앱의 플러그인 형식과 다른 앱의 형식이 달랐고, 도구를 만드는 쪽은 앱마다 따로 붙여야 했다.
MCP 는 이 곱셈을 덧셈으로 바꾼다.
이전: 앱 M 개 × 도구 N 개 = M×N 개의 맞춤 연동
이후: 앱마다 클라이언트 한 번 + 도구마다 서버 한 번 = M + N도구를 만드는 쪽은 MCP 서버를 한 번만 만들면 모든 호환 앱이 그것을 쓴다. 앱을 만드는 쪽은 클라이언트를 한 번만 구현하면 세상의 모든 MCP 서버에 닿는다. 어느 쪽이든 한 번의 구현이 양쪽 전부를 향해 열린다.
이것은 새로운 발상이 아니다. USB 가 주변기기에, 그리고 LSP(Language Server Protocol)가 에디터와 언어 도구 사이에 한 일과 정확히 같다. 에디터가 M 개, 언어가 N 개일 때 LSP 는 그 곱셈을 덧셈으로 접었고, MCP 는 그 패턴을 LLM 의 도구 연결에 그대로 가져왔다. 표준이 자리 잡는 방식에는 계보가 있다.
마무리
MCP 가 표준이 된 것은 가장 똑똑한 규약이어서가 아니다. 새 포맷을 만들지 않아 진입이 쉬웠고, 검증된 M+N 패턴을 빌려 왔고, 무엇보다 통합의 곱셈을 덧셈으로 접었다. 표준은 대개 가장 영리한 것이 아니라 가장 많이 꽂히는 것이 된다.
한 가지만 구분해 두자. 모델이 “이 도구를 부르겠다” 고 정하는 것은 모델 자신의 능력이다. MCP 는 그 도구가 어디에 있고 어떻게 생겼으며 어떻게 실행되는지를 프로세스 경계 너머로 표준화한 규약이다. 둘은 다른 층이고, 한 짝으로 맞물려 돌아간다. 앞 글이 MCP 를 언제 쓰는가의 문제였다면, 이 글은 그 MCP 가 왜 지금의 모양인가의 문제였다. 멍청한 어댑터 하나가 표준이 되기까지, 정작 영리했던 것은 어댑터가 아니라 그것을 잇는 규약의 설계였다.
댓글 0