아키텍처 개요

아키텍처 개요 (Architecture overview)

Model Context Protocol(MCP)의 전체적인 구조를 한눈에 살펴보는 문서예요. MCP의 범위(scope)와 핵심 개념을 다루고, 각 개념을 실제로 보여 주는 예제까지 포함하고 있습니다.

출처: 문서

본문

이 MCP 개요는 범위와 핵심 개념을 다루고, 각 핵심 개념을 보여 주는 예제를 제공합니다.

MCP SDK가 많은 부분을 추상화해 주기 때문에, 대부분의 개발자는 데이터 계층 프로토콜 섹션이 가장 유용할 거예요. 이 섹션은 MCP 서버가 AI 애플리케이션에 어떻게 컨텍스트를 제공할 수 있는지를 다룹니다.

구체적인 구현 세부 사항은 언어별 SDK 문서를 참고하세요.

범위 (Scope)

Model Context Protocol에는 다음과 같은 프로젝트들이 포함됩니다.

  • MCP 사양: 클라이언트와 서버의 구현 요구 사항을 설명하는 MCP의 사양이에요.
  • MCP SDK: MCP를 구현하는 다양한 프로그래밍 언어용 SDK예요.
  • MCP 개발 도구: MCP Inspector를 포함한 MCP 서버·클라이언트 개발 도구예요.
  • MCP 참조 서버 구현: MCP 서버의 참조 구현체들이에요.

MCP는 컨텍스트 교환을 위한 프로토콜에만 집중합니다. AI 애플리케이션이 LLM을 어떻게 사용하고, 주어진 컨텍스트를 어떻게 관리할지는 규정하지 않아요.

MCP의 개념 (Concepts of MCP)

참여자 (Participants)

MCP는 클라이언트-서버 아키텍처를 따르며, MCP 호스트—Claude Code나 Claude Desktop 같은 AI 애플리케이션—가 하나 이상의 MCP 서버에 연결을 맺어요. MCP 호스트는 각 MCP 서버마다 MCP 클라이언트 하나를 만들어 이 연결을 수행합니다. 각 MCP 클라이언트는 대응하는 MCP 서버와 전용 연결을 유지해요.

STDIO 전송을 사용하는 로컬 MCP 서버는 보통 단일 MCP 클라이언트를 서빙하고, Streamable HTTP 전송을 사용하는 원격 MCP 서버는 보통 많은 MCP 클라이언트를 서빙합니다.

MCP 아키텍처의 핵심 참여자는 다음과 같아요.

  • MCP 호스트: 하나 이상의 MCP 클라이언트를 조정하고 관리하는 AI 애플리케이션이에요.
  • MCP 클라이언트: MCP 서버에 연결을 유지하고, MCP 호스트가 사용할 컨텍스트를 MCP 서버로부터 얻는 구성 요소예요.
  • MCP 서버: MCP 클라이언트에 컨텍스트를 제공하는 프로그램이에요.

예를 들어: Visual Studio Code는 MCP 호스트 역할을 해요. Visual Studio Code가 Sentry MCP 서버 같은 MCP 서버에 연결하면, Visual Studio Code 런타임은 Sentry MCP 서버와의 연결을 유지하는 MCP 클라이언트 객체를 인스턴스화해요. 이후 Visual Studio Code가 로컬 파일시스템 서버 같은 다른 MCP 서버에 연결하면, 런타임은 이 연결을 유지하기 위한 추가 MCP 클라이언트 객체를 인스턴스화합니다.

graph TB
    subgraph "MCP Host (AI Application)"
        Client1["MCP Client 1"]
        Client2["MCP Client 2"]
        Client3["MCP Client 3"]
        Client4["MCP Client 4"]
    end

    ServerA["MCP Server A - Local<br/>(e.g. Filesystem)"]
    ServerB["MCP Server B - Local<br/>(e.g. Database)"]
    ServerC["MCP Server C - Remote<br/>(e.g. Sentry)"]

    Client1 ---|"Dedicated<br/>connection"| ServerA
    Client2 ---|"Dedicated<br/>connection"| ServerB
    Client3 ---|"Dedicated<br/>connection"| ServerC
    Client4 ---|"Dedicated<br/>connection"| ServerC

MCP 서버는 어디에서 실행되든 컨텍스트 데이터를 서빙하는 프로그램을 가리킨다는 점에 주의하세요. MCP 서버는 로컬이나 원격에서 실행될 수 있어요. 예를 들어 Claude Desktop이 파일시스템 서버를 실행하면, 이 서버는 STDIO 전송을 사용하기 때문에 같은 머신에서 로컬로 실행돼요. 이를 흔히 "로컬" MCP 서버라고 부르죠. 공식 Sentry MCP 서버는 Sentry 플랫폼에서 실행되며 Streamable HTTP 전송을 사용하는데, 이를 "원격" MCP 서버라고 합니다.

계층 (Layers)

MCP는 두 개의 계층으로 구성돼요.

  • 데이터 계층(Data layer): 클라이언트-서버 통신을 위한 JSON-RPC 기반 프로토콜을 정의해요. 기능(capability)과 버전 발견, 그리고 도구·리소스·프롬프트·알림 같은 핵심 프리미티브가 여기에 포함됩니다.
  • 전송 계층(Transport layer): 클라이언트와 서버 사이의 데이터 교환을 가능하게 하는 통신 메커니즘과 채널을 정의해요. 전송별 연결 설정, 메시지 프레임, 인증이 여기에 포함됩니다.

개념적으로 데이터 계층이 안쪽, 전송 계층이 바깥쪽 계층이에요.

데이터 계층 (Data layer)

데이터 계층은 JSON-RPC 2.0 기반 교환 프로토콜을 구현해서 메시지 구조와 의미론을 정의해요. 이 계층에는 다음이 포함됩니다.

  • 발견(Discovery): 클라이언트가 server/discover 요청으로 서버의 지원 프로토콜 버전, 기능, 정체성을 조회할 수 있게 해 줘요.
  • 서버 기능(Server features): 서버가 핵심 기능을 제공할 수 있게 해 줘요. AI 동작을 위한 도구, 컨텍스트 데이터를 위한 리소스, 클라이언트와의 상호작용 템플릿을 위한 프롬프트가 포함됩니다.
  • 클라이언트 기능(Client features): 서버가 사용자에게 입력을 요청(elicitation)할 수 있게 해 줘요. Sampling은 프로토콜 버전 2026-07-28부터 폐기 예정입니다.
  • 유틸리티 기능(Utility features): 실시간 업데이트를 위한 알림, 장기 실행 작업을 위한 진행 추적 같은 추가 기능을 지원해요.

전송 계층 (Transport layer)

전송 계층은 클라이언트와 서버 사이의 통신 채널과 인증을 관리해요. 연결 설정, 메시지 프레임, MCP 참여자 간 안전한 통신을 처리합니다.

MCP는 두 가지 전송 메커니즘을 지원해요.

  • Stdio 전송: 같은 머신의 로컬 프로세스 간 직접 프로세스 통신에 표준 입력/출력 스트림을 사용해요. 네트워크 오버헤드가 없어 최적의 성능을 제공합니다.
  • Streamable HTTP 전송: 클라이언트-서버 메시지에 HTTP POST를, 스트리밍 기능에는 선택적으로 Server-Sent Events를 사용해요. 이 전송은 원격 서버 통신을 가능하게 하며, bearer 토큰, API 키, 커스텀 헤더를 포함한 표준 HTTP 인증 방식을 지원합니다. MCP는 인증 토큰을 얻기 위해 OAuth 사용을 권장해요.

전송 계층은 프로토콜 계층으로부터 통신 세부 사항을 추상화해서, 모든 전송 메커니즘에서 동일한 JSON-RPC 2.0 메시지 형식을 사용할 수 있게 합니다.

데이터 계층 프로토콜 (Data Layer Protocol)

MCP의 핵심 부분은 MCP 클라이언트와 MCP 서버 사이의 스키마와 의미론을 정의하는 것이에요. 개발자는 데이터 계층—특히 프리미티브 집합—이 MCP에서 가장 흥미로운 부분이라고 느낄 거예요. 이것은 MCP 서버에서 MCP 클라이언트로 컨텍스트를 공유할 수 있는 방법을 정의하는 부분이죠.

MCP는 기반 RPC 프로토콜로 JSON-RPC 2.0을 사용합니다. 클라이언트와 서버는 서로 요청을 보내고 그에 맞게 응답해요. 응답이 필요 없는 경우에는 알림(notification)을 사용할 수 있어요.

무상태성과 발견 (Statelessness and discovery)

MCP는 [무상태 프로토콜](stateless protocol)이에요. 모든 요청은 처리에 필요한 모든 정보를 담고 있어서, 서버는 이전 요청에서 어떤 것도 추론하지 않는다는 뜻이죠. 모든 요청은 사용 중인 프로토콜 버전과 그 요청에 관련된 [기능(capabilities)](도구·리소스·프롬프트 같은 클라이언트나 서버가 지원하는 기능과 작업)을 _meta 필드에 담아 보내므로, 서버는 각 요청을 독립적으로 처리할 수 있어요. 클라이언트는 설정에서 제외하지 않는 한 같은 필드에서 자신의 정체성도 밝혀야 합니다. 서버는 필수 server/discover 요청을 통해 지원 버전과 기능을 광고하며, 클라이언트는 다른 어떤 요청보다 먼저 이 요청을 보낼 수 있어요. 자세한 내용은 사양에서, 요청별 메타데이터와 발견 순서는 예제에서 확인할 수 있어요.

프리미티브 (Primitives)

MCP 프리미티브는 MCP 내에서 가장 중요한 개념이에요. 클라이언트와 서버가 서로 무엇을 제공할 수 있는지를 정의하죠. 이 프리미티브들은 AI 애플리케이션과 공유할 수 있는 컨텍스트 정보의 유형과, 수행할 수 있는 동작의 범위를 규정합니다.

MCP는 서버가 노출할 수 있는 세 가지 핵심 프리미티브를 정의해요.

  • 도구(Tools): AI 애플리케이션이 동작을 수행하기 위해 호출할 수 있는 실행 가능한 함수예요 (예: 파일 작업, API 호출, 데이터베이스 쿼리).
  • 리소스(Resources): AI 애플리케이션에 컨텍스트 정보를 제공하는 데이터 소스예요 (예: 파일 내용, 데이터베이스 레코드, API 응답).
  • 프롬프트(Prompts): 언어 모델과의 상호작용을 구조화하는 데 도움을 주는 재사용 가능한 템플릿이에요 (예: 시스템 프롬프트, few-shot 예시).

각 프리미티브 유형에는 발견(*/list), 검색(*/get), 일부는 실행(tools/call)을 위한 연관 메서드가 있어요. MCP 클라이언트는 */list 메서드를 사용해 사용 가능한 프리미티브를 발견합니다. 예를 들어 클라이언트는 먼저 사용 가능한 모든 도구(tools/list)를 나열한 다음 실행할 수 있어요. 이 설계 덕분에 목록이 동적일 수 있습니다.

구체적인 예로, 데이터베이스에 대한 컨텍스트를 제공하는 MCP 서버를 생각해 보세요. 데이터베이스를 쿼리하는 도구, 데이터베이스 스키마를 담은 리소스, 도구와 상호작용하기 위한 few-shot 예시를 포함한 프롬프트를 노출할 수 있어요.

서버 프리미티브에 대한 자세한 내용은 서버 개념을 참고하세요.

MCP는 클라이언트가 노출할 수 있는 프리미티브도 정의합니다. 이 프리미티브들은 MCP 서버 작성자가 더 풍부한 상호작용을 만들 수 있게 해 줘요.

  • Elicitation: 서버가 사용자에게 추가 정보를 요청할 수 있게 해 줍니다. 서버 작성자가 사용자에게 더 많은 정보를 얻거나, 작업 확인을 요청하고 싶을 때 유용해요. 서버는 elicitation/create 메서드로 사용자 입력을 요청합니다.

Elicitation 요청은 Multi Round-Trip Requests 패턴을 통해 전달되며, elicitation 개요에서 자세히 설명합니다.

폐기 예정(Deprecated): 다음 클라이언트 프리미티브는 프로토콜 버전 2026-07-28부터 폐기 예정입니다.

  • Sampling: 서버가 클라이언트의 AI 애플리케이션에 언어 모델 완성(completion)을 요청할 수 있게 해 줍니다. 서버 작성자가 언어 모델에 접근하고 싶지만 모델 독립성을 유지하고 싶고, MCP 서버에 언어 모델 SDK를 포함하고 싶지 않을 때 유용해요. 서버는 sampling/createMessage 메서드로 완성을 요청하며, 이것 역시 Multi Round-Trip Requests 패턴으로 전달됩니다. 새 구현은 LLM 제공자 API에 직접 통합해야 합니다.
  • Logging: 서버가 디버깅과 모니터링 목적으로 클라이언트에 로그 메시지를 보낼 수 있게 해 줍니다. 새 구현은 stderr(stdio 전송)에 로그를 남기거나 OpenTelemetry를 사용해야 합니다.

클라이언트 프리미티브에 대한 자세한 내용은 클라이언트 개념을 참고하세요.

서버·클라이언트 프리미티브 외에도, 프로토콜은 핵심 프로토콜 위에 구축되는 선택적 확장을 지원합니다. 예를 들어 Tasks 확장은 서버가 장기 실행 요청에 대한 지속적 핸들을 돌려줄 수 있게 해서, 클라이언트가 상태를 폴링하고 나중에 결과를 검색할 수 있게 해 줘요.

알림 (Notifications)

프로토콜은 서버와 클라이언트 사이의 동적 업데이트를 위한 실시간 알림을 지원해요. 예를 들어 서버의 사용 가능한 도구가 변경되면(새 기능이 생기거나 기존 도구가 수정되는 등), 서버는 연결된 클라이언트에 도구 업데이트 알림을 보내 변경 사실을 알릴 수 있어요. 알림은 JSON-RPC 2.0 알림 메시지로 전송되며(응답을 기대하지 않음), 변경 알림은 선택 사항(opt-in)이에요. 클라이언트는 받고 싶은 알림 유형을 이름 붙인 장기 연결 subscriptions/listen 스트림을 열고, 서버는 그 스트림에 해당 알림을 전달해요.

예제 (Example)

데이터 계층 (Data Layer)

이 섹션은 데이터 계층 프로토콜에 초점을 맞춰 MCP 클라이언트-서버 상호작용을 단계별로 살펴봅니다. JSON-RPC 2.0 메시지를 사용해 발견, 도구 작업, 알림을 시연할게요.

단계 1: 발견 (Discovery)

무상태성과 발견 섹션에서 설명했듯, 모든 MCP 요청은 _meta 필드에 프로토콜 버전과 클라이언트 기능을 담고, 클라이언트는 거기에 정체성도 포함해야 해요. 다른 요청을 보내기 전에 서버가 무엇을 지원하는지 알고 싶은 클라이언트는, 모든 서버가 반드시 구현해야 하는 server/discover 요청을 보냅니다. 발견 응답은 보통 캐시 가능한데, 즉 재사용이 가능해서 모든 요청마다 발견 흐름을 수행할 필요가 없다는 뜻이에요.

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "server/discover",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {
        "name": "example-client",
        "version": "1.0.0"
      },
      "io.modelcontextprotocol/clientCapabilities": {
        "elicitation": {}
      }
    }
  }
}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "resultType": "complete",
    "supportedVersions": ["2026-07-28"],
    "capabilities": {
      "tools": {
        "listChanged": true
      },
      "resources": {}
    },
    "_meta": {
      "io.modelcontextprotocol/serverInfo": {
        "name": "example-server",
        "version": "1.0.0"
      }
    },
    "ttlMs": 3600000,
    "cacheScope": "public"
  }
}
발견 교환 이해하기

_meta 필드와 발견 응답은 함께 여러 목적을 제공해요.

  1. 프로토콜 버전 선택: io.modelcontextprotocol/protocolVersion 필드는 이 요청에서 클라이언트가 말하는 버전을 선언하고, 응답의 supportedVersions는 서버가 수락하는 버전을 나열합니다. 서버가 요청된 버전을 지원하지 않으면, 지원하는 버전 목록을 담은 UnsupportedProtocolVersionError로 요청을 거부하고, 클라이언트는 서로 지원하는 버전으로 재시도해요.

  2. 기능 발견: 클라이언트는 매 요청마다 io.modelcontextprotocol/clientCapabilities에 자신의 기능을 선언하고, 서버는 server/discover에서 자신의 capabilities 객체를 돌려줍니다. 이렇게 해서 각 당사자는 상대방이 처리할 수 있는 프리미티브(도구, 리소스, 프롬프트)와 변경 알림 사용 가능 여부를 알 수 있으므로, 지원되지 않는 작업을 시도하지 않게 됩니다.

  3. 정체성 교환: 요청 _meta의 io.modelcontextprotocol/clientInfo 필드와 결과 _meta의 io.modelcontextprotocol/serverInfo 필드는 디버깅과 호환성 목적의 식별·버전 정보를 제공해요.

이 예제에서 교환은 MCP 기능이 어떻게 선언되는지 보여 줍니다.

클라이언트 기능(Client Capabilities):

  • "elicitation": {} — 클라이언트는 서버가 요청할 때 사용자에게 추가 입력을 수집할 수 있다고 선언해요.

서버 기능(Server Capabilities):

  • "tools": {"listChanged": true} — 서버는 도구 프리미티브를 지원하고, subscriptions/listen에서 toolsListChanged 필터를 처리할 수 있어요. 이 필터를 요청한 클라이언트는 도구 목록이 변경될 때 notifications/tools/list_changed를 받습니다.
  • "resources": {} — 서버는 리소스 프리미티브도 지원해요(resources/list와 resources/read 메서드를 처리할 수 있음).

server/discover 호출은 선택 사항입니다. 모든 요청이 같은 _meta 필드를 담기 때문에, 클라이언트는 어떤 요청이든 바로 보내고 버전 오류가 오면 처리할 수 있어요. 발견은 서버의 정체성, 기능, 지원 버전을 한 번의 요청으로 가져오는 편리한 방법이에요.

이것이 AI 애플리케이션에서 어떻게 동작하나요?

AI 애플리케이션의 MCP 클라이언트 매니저는 설정된 서버에 연결하고, 발견된 기능을 나중에 사용하기 위해 저장해요. 애플리케이션은 이 정보를 사용해 어떤 서버가 특정 유형의 기능(도구, 리소스, 프롬프트)을 제공하고 실시간 업데이트를 지원하는지 판단합니다. Python SDK에서는 클라이언트가 연결될 때 발견이 일어나며, 결과는 클라이언트 객체에서 사용할 수 있어요.

# Pseudo Code
async with Client(stdio_client(server_config)) as client:
    if client.server_capabilities.tools:
        app.register_mcp_server(client, supports_tools=True)
    app.set_server_ready(client)

단계 2: 도구 발견 (Tool Discovery / Primitives)

클라이언트는 tools/list 요청을 보내 사용 가능한 도구를 발견할 수 있어요. 이 요청은 MCP의 도구 발견 메커니즘의 핵심입니다. 사용하려는 도구가 무엇인지 미리 파악할 수 있게 해 주죠.

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/list",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {
        "name": "example-client",
        "version": "1.0.0"
      },
      "io.modelcontextprotocol/clientCapabilities": {
        "elicitation": {}
      }
    }
  }
}
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "resultType": "complete",
    "tools": [
      {
        "name": "calculator_arithmetic",
        "title": "Calculator",
        "description": "Perform mathematical calculations including basic arithmetic, trigonometric functions, and algebraic operations",
        "inputSchema": {
          "type": "object",
          "properties": {
            "expression": {
              "type": "string",
              "description": "Mathematical expression to evaluate (e.g., '2 + 3 * 4', 'sin(30)', 'sqrt(16)')"
            }
          },
          "required": ["expression"]
        }
      },
      {
        "name": "weather_current",
        "title": "Weather Information",
        "description": "Get current weather information for any location worldwide",
        "inputSchema": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "City name, address, or coordinates (latitude,longitude)"
            },
            "units": {
              "type": "string",
              "enum": ["metric", "imperial", "kelvin"],
              "description": "Temperature units to use in response",
              "default": "metric"
            }
          },
          "required": ["location"]
        }
      }
    ],
    "ttlMs": 300000,
    "cacheScope": "public"
  }
}
도구 발견 요청 이해하기

tools/list 요청은 모든 MCP 요청에 함께 오는 표준 _meta 필드 외에 추가 매개변수가 필요 없어요. 페이지네이션을 위한 선택적 cursor 매개변수도 받지만, 위 예제에는 생략되어 있어요.

도구 발견 응답 이해하기

응답에는 각 사용 가능한 도구에 대한 종합 메타데이터를 제공하는 tools 배열이 포함돼요. 이 배열 기반 구조 덕분에 서버는 여러 도구를 동시에 노출하면서도 서로 다른 기능 사이의 명확한 경계를 유지할 수 있어요.

응답의 각 도구 객체에는 몇 가지 핵심 필드가 포함됩니다.

  • name: 서버 네임스페이스 안에서 도구를 식별하는 고유 식별자예요. 도구 실행의 기본 키 역할을 하며 명확한 명명 패턴을 따라야 해요 (예: calculate 대신 calculator_arithmetic).
  • title: 클라이언트가 사용자에게 보여 줄 수 있는 사람이 읽을 수 있는 도구 표시 이름이에요.
  • description: 도구가 무엇을 하고 언제 사용하는지에 대한 상세한 설명이에요.
  • inputSchema: 기대하는 입력 매개변수를 정의하는 JSON Schema로, 타입 검증을 가능하게 하고 필수·선택 매개변수에 대한 명확한 문서를 제공해요.

결과는 "resultType": "complete"로 표시되고 두 개의 캐싱 필드를 담아요. ttlMs는 밀리초 단위의 신선도 힌트라서, 이 도구 목록을 5분 동안 캐시할 수 있어요. cacheScope는 누가 응답을 재사용할 수 있는지 나타냅니다. 전체 규칙은 사양의 캐싱 유틸리티에 정의돼 있어요.

이것이 AI 애플리케이션에서 어떻게 동작하나요?

AI 애플리케이션은 연결된 모든 MCP 서버에서 사용 가능한 도구를 가져와서, 언어 모델이 접근할 수 있는 통합 도구 레지스트리로 결합해요. 이렇게 하면 LLM이 수행할 수 있는 동작을 이해하고, 대화 중 적절한 도구 호출을 자동으로 생성할 수 있어요.

# Pseudo-code using MCP Python SDK patterns
available_tools = []
for client in app.mcp_clients():
    tools_response = await client.list_tools()
    available_tools.extend(tools_response.tools)
conversation.register_available_tools(available_tools)

여러 서버를 연합하는 클라이언트는 모든 도구를 처음부터 로드하는 대신 점진적 도구 발견을 사용할 수 있어요.

단계 3: 도구 실행 (Tool Execution / Primitives)

클라이언트는 이제 tools/call 메서드로 도구를 실행할 수 있어요. 이는 MCP 프리미티브가 실제로 어떻게 사용되는지 보여 줍니다. 사용 가능한 도구를 발견한 뒤 적절한 인수로 호출할 수 있죠.

도구 실행 요청 이해하기

tools/call 요청은 타입 안전성과 클라이언트-서버 간 명확한 통신을 보장하는 구조화된 형식을 따라요. 발견 응답에서 가져온 올바른 도구 이름(weather_current)을 사용하고 있다는 점에 주목하세요. 단순화된 이름이 아니라요.

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "weather_current",
    "arguments": {
      "location": "San Francisco",
      "units": "imperial"
    },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {
        "name": "example-client",
        "version": "1.0.0"
      },
      "io.modelcontextprotocol/clientCapabilities": {
        "elicitation": {}
      }
    }
  }
}
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "resultType": "complete",
    "content": [
      {
        "type": "text",
        "text": "Current weather in San Francisco: 68°F, partly cloudy with light winds from the west at 8 mph. Humidity: 65%"
      }
    ]
  }
}
도구 실행의 핵심 요소

요청 구조에는 몇 가지 중요한 구성 요소가 있어요.

  1. name: 발견 응답의 도구 이름(weather_current)과 정확히 일치해야 해요. 서버가 어떤 도구를 실행할지 정확히 식별할 수 있게 해 주죠.

  2. arguments: 도구의 inputSchema가 정의한 입력 매개변수를 담아요. 예제에서는:

    • location: "San Francisco" (필수 매개변수)
    • units: "imperial" (선택 매개변수, 지정하지 않으면 기본값 "metric")
  3. _meta: 모든 MCP 요청이 포함해야 하는 표준 요청별 필드(프로토콜 버전, 클라이언트 기능)와, 설정에서 제외하지 않는 한 클라이언트가 포함해야 하는 정체성을 담아요.

  4. JSON-RPC 구조: 요청-응답 상관을 위한 고유 id가 있는 표준 JSON-RPC 2.0 형식을 사용해요.

도구 실행 응답 이해하기

응답은 MCP의 유연한 콘텐츠 시스템을 보여 줍니다.

  1. content 배열: 도구 응답은 콘텐츠 객체 배열을 반환해서 풍부하고 다중 형식의 응답(텍스트, 이미지, 리소스 등)을 가능하게 해요.
  2. 콘텐츠 유형: 각 콘텐츠 객체에는 type 필드가 있어요. 예제에서 "type": "text"는 일반 텍스트 콘텐츠를 나타내지만, MCP는 다양한 사용 사례를 위한 다양한 콘텐츠 유형을 지원합니다.
  3. 구조화된 출력: 응답은 AI 애플리케이션이 언어 모델 상호작용의 컨텍스트로 사용할 수 있는 실행 가능한 정보를 제공해요.

이 실행 패턴 덕분에 AI 애플리케이션은 서버 기능을 동적으로 호출하고, 언어 모델과의 대화에 통합할 수 있는 구조화된 응답을 받을 수 있어요.

이것이 AI 애플리케이션에서 어떻게 동작하나요?

언어 모델이 대화 중 도구를 사용하기로 결정하면, AI 애플리케이션은 도구 호출을 가로채서 적절한 MCP 서버로 라우팅하고 실행한 다음, 그 결과를 대화 흐름의 일부로 LLM에 돌려줍니다. 이렇게 해서 LLM은 실시간 데이터에 접근하고 외부 세계에서 동작을 수행할 수 있어요.

# Pseudo-code for AI application tool execution
async def handle_tool_call(conversation, tool_name, arguments):
    client = app.find_mcp_client_for_tool(tool_name)
    result = await client.call_tool(tool_name, arguments)
    conversation.add_tool_result(result.content)

단계 4: 실시간 업데이트 (Real-time Updates / Notifications)

MCP는 서버가 폴링 없이 변경 사실을 클라이언트에 알릴 수 있는 실시간 알림을 지원해요. 클라이언트를 동기화되고 반응적으로 유지하는 핵심 기능인 알림 시스템을 보여 줍니다.

변경 구독하기

변경 알림은 선택 사항(opt-in)이에요. 이를 받으려면 클라이언트가 받고 싶은 이벤트 유형을 이름 붙인 notifications 필터와 함께 subscriptions/listen 요청을 보내 장기 연결 알림 스트림을 열어야 해요. 여기서 클라이언트는 도구 목록 변경을 요청합니다.

{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "subscriptions/listen",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {
        "name": "example-client",
        "version": "1.0.0"
      },
      "io.modelcontextprotocol/clientCapabilities": {
        "elicitation": {}
      }
    },
    "notifications": {
      "toolsListChanged": true
    }
  }
}

모든 클라이언트 요청은 _meta에 io.modelcontextprotocol/protocolVersion과 io.modelcontextprotocol/clientCapabilities 필드를 담고, 보통 io.modelcontextprotocol/clientInfo도 담아요. 그래서 서버는 연결 상태에 의존하지 않고 클라이언트를 식별할 수 있어요.

서버는 notifications/subscriptions/acknowledged로 구독을 승인하는데, 이는 _meta에 그 구독의 ID를 담는 첫 번째 메시지예요(서버는 그 전에 그 구독에 대한 다른 알림을 보내지 않습니다). 이 메시지의 notifications 필드는 서버가 처리하기로 동의한 요청 필터의 부분집합을 반영하며, 지원되지 않는 알림 유형은 생략돼요.

{
  "jsonrpc": "2.0",
  "method": "notifications/subscriptions/acknowledged",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/subscriptionId": 4
    },
    "notifications": {
      "toolsListChanged": true
    }
  }
}
도구 목록 변경 알림 이해하기

승인 후, 서버의 사용 가능한 도구가 변경되면(새 기능이 생기거나, 기존 도구가 수정되거나, 도구가 일시적으로 사용 불가해지는 경우 등) 서버는 그 스트림에 알림을 전달해요.

{
  "jsonrpc": "2.0",
  "method": "notifications/tools/list_changed",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/subscriptionId": 4
    }
  }
}
MCP 알림의 핵심 특징
  1. 응답 불필요: 알림에는 id 필드가 없다는 점을 주목하세요. 이는 응답이 기대되거나 전송되지 않는 JSON-RPC 2.0 알림 의미론을 따르는 것입니다.
  2. 선택 기반(Opt-In): 이 알림은 subscriptions/listen 필터에서 "toolsListChanged": true를 요청한 클라이언트에게만 전송되며, 도구 기능에서 "listChanged": true를 선언한 서버에서만 사용할 수 있어요(1단계에서 확인).
  3. 구독-ID 태깅: 스트림의 모든 알림은 _meta에 io.modelcontextprotocol/subscriptionId를 담아요. 값은 스트림을 연 subscriptions/listen 요청의 JSON-RPC ID(예제에서 4)로, 클라이언트는 각 알림을 만든 구독과 연관 지을 수 있어요.
  4. 이벤트 주도: 서버는 내부 상태 변경에 따라 언제 알림을 보낼지 결정해서, MCP 연결을 동적이고 반응적으로 만들어요.
  5. 베스트 에포트(Best Effort): 특히 전송 재연결 사이에서 모든 알림이 전송되거나 수신된다는 보장은 없어요. 클라이언트는 결과의 신선도를 유지하기 위해 폴링에도 의존해야 합니다.
클라이언트의 알림 대응

알림을 받으면 클라이언트는 보통 업데이트된 도구 목록을 요청함으로써 반응해요. 이렇게 하면 클라이언트가 파악한 사용 가능한 도구를 최신으로 유지하는 갱신 주기가 생깁니다.

{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "tools/list",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {
        "name": "example-client",
        "version": "1.0.0"
      },
      "io.modelcontextprotocol/clientCapabilities": {
        "elicitation": {}
      }
    }
  }
}
알림이 왜 중요한가요?

이 알림 시스템은 여러 이유로 중요합니다.

  1. 동적 환경: 도구는 서버 상태, 외부 의존성, 사용자 권한에 따라 나타났다 사라질 수 있어요.
  2. 효율성: 클라이언트는 변경을 폴링할 필요가 없어요. 업데이트가 발생하면 알림을 받죠.
  3. 일관성: 클라이언트가 서버 기능에 대한 정확한 정보를 항상 가지도록 보장해요.
  4. 실시간 협업: 변화하는 컨텍스트에 적응할 수 있는 반응형 AI 애플리케이션을 가능하게 해 줘요.

이 알림 패턴은 도구를 넘어 다른 MCP 프리미티브에도 적용되어, 클라이언트와 서버 사이의 포괄적인 실시간 동기화를 가능하게 합니다.

이것이 AI 애플리케이션에서 어떻게 동작하나요?

AI 애플리케이션은 관심 있는 변경을 위해 알림 스트림을 열어 두고, 알림이 오면 즉시 도구 레지스트리를 갱신하고 LLM의 사용 가능 기능을 업데이트해요. 이렇게 하면 진행 중인 대화가 항상 가장 최신의 도구 집합에 접근할 수 있고, LLM은 새 기능이 생기면 동적으로 적응할 수 있어요.

# Pseudo-code for AI application notification handling
async def follow_tool_changes(client):
    async with client.listen(tools_list_changed=True) as sub:
        async for _event in sub:
            tools_response = await client.list_tools()
            app.update_available_tools(client, tools_response.tools)
            if app.conversation.is_active():
                app.conversation.notify_llm_of_new_capabilities()

더 알아보기 (Learn more)