Сегодня ядром данной статьи будет MCP — как мост между бекендом‑оберткой с LLM и внешними источниками, но при этом я также затрону смежные темы, чтобы картина была полной и не требовалось дополнительно гуглить.

Я постараюсь не давать устоявшиеся термины в контексте MCP, а также в процессе буду пояснять некоторые «базовые» термины, которые все как бы понимают — но нередко нет, чтобы мы все улавливали один и тот же контекст статьи.

Введение

MCP — это и спецификация (прим. нормативное описание того, как что‑то должно работать: термины, требования, форматы, правила совместимости) и протокол (прим. набор согласованных правил взаимодействия между участниками системы: какие сообщения можно посылать, в каком формате, в какой последовательности, какие ответы/ошибки ожидаются, как устроены состояния — handshake, запрос‑ответ и так далее).

MCP как протокол — это что происходит на «проводе/канале»:

  • какие JSON‑RPC методы существуют (например, resources/list, resources/read, tools/call и тому подобное)

  • какие поля и типы данных в запросах/ответах

  • какие правила последовательности: инициализация, capabilities, обработка ошибок, пагинация, стриминг (если поддерживается), закрытие соединения

  • какие есть официальные привязки к транспортам (stdio/HTTP и тому подобное) и их особенности

То есть протокол = поведение + обмен сообщениями.

MCP как спецификация — это «документ, который это всё фиксирует»:

  • формальное описание протокола (то, что выше)

  • общая модель сущностей (tools/resources/prompts и тому подобное)

  • определения схем данных/ошибок

  • требования совместимости/версирования

  • рекомендации по безопасности, примеры, пояснения

То есть спецификация = текст/стандарт, который описывает и нормирует протокол.

Границы применимости

MCP не является универсальной заменой REST/OpenAPI и не пытается описать произвольные веб‑API. MCP задаёт контракт, ориентированный на взаимодействие LLM: listing/reading контекстных ресурсов, вызовы инструментов, выдача готовых промптов, а также (в client features) запросы на sampling или roots при наличии поддержки.

MCP также не навязывает UI: спецификация неоднократно подчёркивает, что конкретные UX‑паттерны (как показывать списки, как подтверждать действия) — ответственность реализаций/хостов.


В основе протокольной части MCP лежит JSON‑RPC 2.0. Дальше раскрою понимание JSON‑RPC 2.0 (если уверены в своем знании — можно просто пропустить).

Пони��ание REST, RPC и JSON-RPС 2.0

REST

Начнем с REST, предполагая, что он уже известен всем. REST нам предлагает мыслить в контексте объектов, вот есть заказ (Order), с ним можно делать следующее:

  • Создать, Получить, Изменить и Удалить (тот самый CRUD). Отсюда и HTTP методы (границы для работы с объектом) GET,POST,PUT/PATCH,DELETE;

  • Обращаться к нему, через его имя: /orders/123.

Основная идея: обращение к ресурсу (в URL), и работа с ним через заданные операции. Важно: REST старается держать ограниченный набор операций вокруг ресурсов, отсюда получаются следующее:

  • Плюсы: стандартизация, кеширование, совместимость с HTTP‑инструментами;

  • Минусы: нередко требуются обходы CRUD границ у ресурса и это порождает костыль (по идее временное, но часто — нет, решение проблемы, которое не вписывается в логику и структуру программы, но позволяет обойти ограничение).

RPC

Здесь скорее парадигма будет не про объект, а про функцию, то есть в первую очередь идёт не объект, а функция. К примеру, мы можем просто осуществить вызов calculatePrice(orderId), то есть обычно у тебя один URL, а уже внутри запроса ты пишешь что вызвать:'

{ "method": "orders.cancel", "params": { "id": 123 } }

Основная идея: все — это вызов метода/функции.

Все в контексте мира HTTP/CDN.

Почему у REST в плюсах есть кеширование? В REST обычно чтение происходит через GET,HEAD по стабильному URL ресурса (GET /users/1), для GET,HEAD есть правила кеширования (Cache‑Control, ETag, Last‑Modified, etc) — это описано в стандарте HTTP caching.

Почему RPC в некоторых кейсах не кешируется? RPC поверх HTTP часто выглядит как POST /rpc (то есть один эндпоинт) и уже в теле {"method": "getUser", "params": {"id": 1}}, отсюда две проблемы для кеширования:

  • Метод POST, по умолчанию, считается unsafe (так как может менять состояние), поэтому промежуточные кеши ведут себя осторожно и не кешируют, а при успешных запросах даже обязаны инвалидировать кеш для затронутого URI.

  • Ключ кеша у shared HTTP caches обычно строится как URL+заголовки+метод, а тело туда не входит. И получается, что для CDN все запросы как одно и то же: POST /rpc ⇒ безопасно кэшировать сложно без дополнительной логики.

JSON-RPC 2.0

Это RPC протокол, где собственно все сообщения — JSON и есть стандартная обертка запроса/ответа, к тому же он ещё и transport‑agnostic (его можно гонять поверх HTTP, сокетов, stdio и т.д). Подробнее здесь.

Разберем Request, его поля:

  • jsonrpc: всегда "2.0"

  • method: имя вызываемого метода (строка)

  • params: аргументы (объект или массив) — опционально

  • id: идентификатор запроса (строка/число). Если id нет — это notification (ответ не ожидается)

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sum",
  "params": {"a": 1, "b": 2}
}

А Response же выглядит так:

  • Успех: result

  • Ошибка: error: { code, message, data? }нет result). Стандартные коды ошибок (например -32601 Method not found, -32602 Invalid params) задаёт спецификация JSON‑RPC.

  • id в ответе должен совпадать с id запроса.

/ Успешный ответ
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": 3
}

/ Ошибка
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": { "code": -32602, "message": "Invalid params" }
}

Почему id в ответе должен совпадать с id запроса? На уровне транспорта обычно один общий канал байтов, по которому летят несколько логических запросов и ответов. Транспорт сам по себе не гарантирует — «именно на X запрос придёт ответ», поэтому нужен id как корреляционный ключ.

В REST каждый запрос — это отдельное HTTP‑сообщение, а ответ структурно привязан к запросу на уровне протокола:

  • HTTP/1.1 без pipelining: запрос отправили → ответ пришёл → следующий.

  • HTTP/1.1 с pipelining (редко): ответы обязаны идти в том же порядке, что и запросы.

  • HTTP/2/HTTP/3: можно много запросов параллельно, ответы могут идти в любом порядке, но у HTTP/2/3 есть встроенный идентификатор потока (stream id), и стек HTTP сам правильно «склеивает» ответ с нужным запросом.

Как это связано с MCP? А в MCP операции вида resources/list, resources/read, resources/templates/list, completion/complete и тому подобное — это JSON‑RPC методы (та самая строка method, а параметры уходят в params).

Важно: MCP использует JSON‑RPC 2.0 как формат сообщений, но накладывает поверх него дополнительные ограничения, чтобы сессия была предсказуемой и проще дебажилась.

  1. Любой request в MCP обязан иметь id (строка или число). Это значит, что request без id в MCP не используется — объект без id трактуется как notification (one‑way), на который запрещено отвечать.

  2. В отличие от чистого JSON‑RPC, в MCP id не может быть null.

  3. Идентификатор запроса не должен повторяться отправителем в рамках одной сессии: иначе корреляция ответов становится неоднозначной.

Эти детали кажутся мелочью, но они напрямую влияют на реализацию: таймауты, ретраи, параллелизм и сопоставление ответов с запросами обычно ломаются здесь.


Жизненный цикл MCP-сессии: initialize → initialized → работа → завершение

Чтобы дальше сущности не висели в воздухе, зафиксируем минимальный жизненный цикл любой MCP-сессии. Он обязателен и одинаков независимо от того, stdio это или HTTP-транспорт.

  1. Клиент отправляет запрос initialize, где сообщает:

    1. версию протокола (например, "2025-03-26"),

    2. свои возможности (client capabilities: например, roots/sampling),

    3. информацию о реализации (name/version).

  2. Сервер отвечает на initialize и возвращает:

    1. согласованную версию протокола,

    2. свои возможности (server capabilities: tools/resources/prompts/logging/completions и т.д.),

    3. serverInfo и (опционально) instructions.

  3. После успешного initialize клиент обязан отправить notification notifications/initialized. Это сигнал готовности: до него сервер не должен начинать нормальную работу (кроме ping/logging в оговорённых случа��х).

  4. Дальше начинается операционная фаза: list/read/call, подписки, нотификации и т.п.

  5. Завершение — это закрытие underlying transport (закрыли stdin / закрыли HTTP-соединение). Отдельного shutdown method на уровне MCP не требуется.

Рисунок 1. Жизненный цикл MCP сессии
Рисунок 1. Жизненный цикл MCP сессии

Транспорты MCP: stdio и Streamable HTTP

MCP определяет стандартные транспорты, которые переносят JSON‑RPC сообщения:

  1. stdio
    Клиент запускает MCP‑сервер как subprocess и общается через stdin/stdout.Практический плюс: минимальная поверхность атаки и простая локальная интеграция. Практический минус: это IPC внутри машины — удобнее для desktop/локальных инструментов, хуже для удалённых серверов.

    Важная деталь реализации: сообщения разделяются переводом строки (newline‑delimited), и сообщение не должно содержать «встроенных» переносов строки — иначе парсер ломает границы сообщений.

  2. Streamable HTTP
    Это современный стандартный HTTP‑транспорт MCP. Клиент отправляет каждое JSON‑RPC сообщение отдельным POST на единый MCP endpoint (например, /mcp). Сервер может (опционально) использовать SSE, чтобы отправлять поток сообщений от сервера к клиенту (но концептуально это всё ещё MCP поверх HTTP).

    Важные security‑грабли для Streamable HTTP:

    1. сервер обязан валидировать Origin, иначе возможны атаки DNS rebinding на локально запущенные MCP‑серверы;

    2. при локальном запуске серверу лучше слушать только localhost (127.0.0.1), а не 0.0.0.0;

    3. для удалённого доступа почти всегда нужна аутентификация.

Если держать в голове эти свойства транспорта, дальше проще понимать: где сессия, почему появляются нотификации, и почему безопасность MCP — это не только про tool calling, но и про сам канал связи.

Далее разберем основные сущности и их взаимодействия.

Сущности и потоки

Взаимодействие идет через три уровня:

  1. Host: «LLM application», в которой живёт пользовательский опыт и которая содержит в себе MCP clients;

  2. Client: «коннектор» внутри host, который поддерживает 1:1 соединение с конкретным MCP server (один экземпляр MCP‑клиента держит одно изолированное соединение/сессию ровно с одним конкретным MCP‑сервером. Хост может поднять много таких клиентов — по одному на каждый сервер, чтобы сохранить изоляцию и независимый lifecycle соединений );

  3. Server: процесс/сервис, предоставляющий инструменты, ресурсы и промпты.

Рисунок 2. Верхнеуровневый вид взаимодействия по MCP
Рисунок 2. Верхнеуровневый вид взаимодействия по MCP

Тут, в целом, должно быть понятно, давайте перейдем к сущностям в самом MCP. Будем отталкиваться от рассмотрения типового потока, который представляет :

  1. Выбор источника внешних возможностей
    Хост понимает, что ему нужно выйти за пределы локального контекста (данные/действия/шаблоны), выбирает подходящий MCP‑сервер(а). Решение обратиться может быть на основе эвристик, конфига, выбора юзера или подсказке от LLM.

  2. Установка сессии и описание того, что сервер умеет
    Хост подключается к серверу и получает каталог «возможностей»:

    1. что можно прочитать (контекст/артефакты);

    2. что можно выполнить (действия);

    3. какие есть готовые шаблоны взаимодействия (подсказки/промпты);

    4. (иногда) возможность обратного запроса к LLM через хост.

  3. Планирование использования возможностей
    LLM (и/или пользователь) предлагает, какие возможности нужны, а хост решает и применяет политику: что разрешено, что спросить у пользователя, что логировать, что ограничить.

  4. Получение внешней информации и/или выполнение действий
    Хост: подтягивает нужные данные как контекст (файлы/документы/записи/и тому подобное) и/или запускает действия (получить ответ сервиса, что‑то посчитать, сделать запрос, создать объект и тому подобное) и получает результаты в удобном формате.

  5. Сборка рабочего контекста
    Хост упаковывает полученное в контекст для размышления (фрагменты текста, структурированные данные, вложения) и формирует запрос к LLM.

  6. Рассуждение и итерация
    LLM отвечает на основе собранного контекста. Если в процессе выясняется, что нужно ещё — цикл повторяется: снова план → получить/выполнить → обновить контекст → продолжить.

  7. (Опционально) Обратный ход
    Иногда сервер не только отдаёт/делает, но и просит хост получить у LLM кусок генерации/решения и хост решает, можно ли это выполнять.

Рисунок 3. Типовой поток работы по MCP
Рисунок 3. Типовой поток работы по MCP

Собственно все разбираемые дальше сущности будут олицетворением того, что было описано выше и, по сути, будут представлены технические детали реализации в MCP. Я также буду ссылаться к представленному потоку, чтобы вы могли понять какую роль та или иная сущность играет в взаимодействии, ссылка будет в формате «поток-2.1».

Resources - контекстные данные

Это данные/контент, который сервер делает доступным клиенту, чтобы клиент мог использовать это как контекст для LLM‑взаимодействий: файлы, схемы БД, документы приложения и так далее.

Каждый resource уникально идентифицируется URI (uri: string). Это не обязательно веб‑адрес: URI может быть file://..., git://... или кастомной схемой (например db://...) — MCP это допускает.

Список доступных ресурсов можно получить через обращение JSON‑RPC метод "resources/list" и он поддерживает cursor‑pagination (cursornextCursor).

Замечание про capabilities и realtime:

  • Поддержка ресурсов объявляется сервером в capabilities (в ответе на initialize). Там же сервер явно указывает, умеет ли он:

    • слать нотификацию о смене списка (listChanged);

    • поддерживать подписки на изменения конкретного ресурса (subscribe).

  • Если включён listChanged, сервер шлёт точную нотификацию:
    notifications/resources/list_changedи клиент обычно реагирует пере-вычитыванием каталога через resources/list.

  • Если включён subscribe, клиент подписывается на конкретный uri через resources/subscribe, а сервер присылает notifications/resources/updated {uri}.

Ещё деталь, которую полезно знать при UI/UX: resources и content-блоки могут иметь annotations (подсказки клиенту) — например audience (user/assistant), priority и lastModified. Это не “логика протокола”, но сильно влияет на то, как удобно показывать ресурсы в интерфейсе.

Про cursor‑pagination — в первом запросе клиент либо не передает cursor, либо ставит cursor: null, сервер возвращает первую страницу (resources: [...]) и, если есть продолжение, кладёт в ответ nextCursor — это непрозрачный токен (буквально — продолжить отсюда); дальше клиент делает следующий запрос тем же методом, но уже с params: {"cursor": "<nextCursor из прошлого ответа>"}, и снова получает очередную порцию ресурсов и новый nextCursor; цикл повторяется, пока сервер не перестанет присылать nextCursor (или пришлёт null) — это означает, что страниц больше нет. «поток-2.1»

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "resources/list",
  "params": {
    "cursor": "optional-cursor-value"
  }
}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "resources": [
      {
        "uri": "file:///project/src/main.rs",
        "name": "main.rs",
        "title": "Rust Software Application Main File",
        "description": "Primary application entry point",
        "mimeType": "text/x-rust"
      }
    ],
    "nextCursor": "next-page-cursor"
  }
}

MIME type (media type) — стандартный идентификатор формата данных вроде:

  • text/plain

  • application/json

  • text/markdown

  • image/png

  • application/pdf

Он нужен, чтобы получатель понимал формат и как его обрабатывать. (В HTTP это то же самое поле Content-Type.) Подробнее здесь.

В MCP у ресурсов и их содержимого есть опциональный mimeType, чтобы клиент мог: корректно отобразить (markdown vs plain text), понять что это бинарный файл (png/pdf) или выбрать обработчик/рендерер.

Чтение ресурса — метод "resources/read" с параметром uri. Ответ содержит contents[], где каждый элемент может быть: text (для текстового контента) или blob (base64 для бинарного) с опциональным mimeType. «поток-4»

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "resources/read",
  "params": {
    "uri": "file:///project/src/main.rs"
  }
}
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "contents": [
      {
        "uri": "file:///project/src/main.rs",
        "mimeType": "text/x-rust",
        "text": "fn main() {\n    println!(\"Hello world!\");\n}"
      }
    ]
  }
}

Resource templates

Это способ описать динамические/параметризованные ресурсы через uriTemplate (URI Template — это строка с выражениями {...}, которые расширяются значениями переменных), чтобы клиент мог конструировать валидные uri под конкретные значения параметров.

Шаблоны также перечисляются через метод "resources/templates/list" и он тоже может быть пагинируемым. «поток-2.3»

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "resources/templates/list",
  "params": {
    "cursor": "optional-cursor-value"
  }
}
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "resourceTemplates": [
      {
        "uriTemplate": "file:///{path}",
        "name": "Project Files",
        "title": "📁 Project Files",
        "description": "Access files in the project directory",
        "mimeType": "application/octet-stream"
      }
    ],
    "nextCursor": "next-page-cursor"
  }
}

Чтобы подставлять валидные аргументы в шаблоны в MCP есть completion API — метод completion/complete, который возвращает подсказки автодополнения аргументов: для prompts (пока их не разбирали) и для наших resource URI templates (в ссылке ref/resource). «поток-2.3»

Идея: пользователь набирает аргумент, а клиент показывает варианты:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "completion/complete",
  "params": {
    "ref": {
      "type": "ref/resource",
      "uri": "file:///{path}"
    },
    "argument": {
      "name": "path",
      "value": "src/ma"
    } 
  }
}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "completion": {
      "values": ["src/main.rs", "src/main_test.rs"],
      "total": 10,
      "hasMore": true
    }
  }
}

Другие сущности (Prompts, Tools)

Prompts. Если resource/template отвечает на вопрос «какой контент можно прочитать/подставить в контекст?», то prompt отвечает на вопрос «какую заготовку диалога/инструкций можно использовать?».

  • как и resources/list, у prompts есть prompts/list (тоже с cursor‑pagination). «поток-2.1»

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "prompts/list",
  "params": {
    "cursor": "optional-cursor-value"
  }
}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "prompts": [
      {
        "name": "code_review",
        "title": "Request Code Review",
        "description": "Asks the LLM to analyze code quality and suggest improvements",
        "arguments": [
          {
            "name": "code",
            "description": "The code to review",
            "required": true
          }
        ]
      }
    ],
    "nextCursor": "next-page-cursor"
  }
}
  • аналогично resources/read(uri) (получить содержимое), у prompts есть prompts/get(name, arguments) — в ответ сервер отдаёт готовый набор сообщений (messages), который хост может напрямую вставить в диалог с LLM. «поток-4»

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "prompts/get",
  "params": {
    "name": "code_review",
    "arguments": {
      "code": "def hello():\n    print('world')"
    }
  }
}
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "description": "Code review prompt",
    "messages": [
      {
        "role": "user",
        "content": {
          "type": "text",
          "text": "Please review this Python code:\n def hello():\n    print('world')"
        }
      }
    ]
  }
}
  • prompts в MCP описаны как user‑controlled (идея: пользователь явно выбирает prompt, например как slash‑команду).

Tools. Если resource/prompts— это читать, то tool — это про выполнить.

  • получение tools/list (тоже с cursor‑pagination). У tool есть inputSchema (JSON Schema) — это ключевое отличие от resources/prompts, потому что tool — это функция с формальным интерфейсом. «поток-2.2»

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {
    "cursor": "optional-cursor-value"
  }
}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "get_weather",
        "title": "Weather Information Provider",
        "description": "Get current weather information for a location",
        "inputSchema": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "City name or zip code"
            }
          },
          "required": ["location"]
        }
      }
    ],
    "nextCursor": "next-page-cursor"
  }
}
  • использование идет через tools/call(name, arguments) — сервер выполняет операцию и возвращает результат как набор content‑блоков (текст, изображение, аудио, ссылки на ресурсы и так далее). «поток-4»

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {
      "location": "New York"
    }
  }
}
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Current weather in New York:\n Temperature: 72°F\n Conditions: Partly cloudy"
      }
    ],
    "isError": false
  }
}
  • tools описаны как model‑controlled (модель может выбирать и инициировать вызов), при этом спецификация отдельно подчёркивает human‑in‑the‑loop (подробнее о данном концепте здесь) как рекомендуемую практику для безопасности

Подписки

В MCP есть два разных механизма реального времени.

Первый это «список изменился» — уведомление, после которого клиент сам перечитывает каталог. Это относится к resources / prompts / tools.

  1. На handshake сервер объявляет capability listChanged для соответствующей подсистемы.

  2. Когда сервер решает, что каталог изменился (добавились/исчезли/изменились элементы), он отправляет JSON‑RPC notification (без id): notifications/*/list_changed

  3. Клиентская реакция почти всегда одна: обновить кеш каталога, то есть снова вызвать */list. В доках по архитектуре это прямо описывается как типичная реакция на notifications/tools/list_changed.

Второе же «изменился конкретный ресурс» — настоящая подписка на объект (только для resources). Это относится только к resources (не к prompts/tools).

  1. Сервер объявляет capability resources: { subscribe: true }.

  2. Клиент подписывается на конкретный uri: resources/subscribe { uri }

  3. Когда содержимое ресурса меняется, сервер шлёт notification: notifications/resources/updated { uri }

  4. Клиент обычно делает lazy load: перечитывает ресурс через resources/read(uri) только когда это реально нужно (или обновляет кеш сразу — зависит от стратегии).

  5. Чтобы перестать слушать изменения: resources/unsubscribe { uri }

В итоге, типичная система взаимодействия будет выглядеть так:

Рисунок 4. Система взаимодействия по MCP, разделенная на операции
Рисунок 4. Система взаимодействия по MCP, разделенная на операции

Авторизация в MCP (важно для HTTP-транспорта)

Авторизация в MCP — опциональная часть стандарта, но для HTTP‑сценариев она быстро становится обязательной практикой: иначе любой, кто достучался до MCP ручки, может вызвать tools и прочитать resources.

Ключевая идея: для HTTP‑транспорта MCP описывает авторизацию на базе OAuth‑подхода (под множество сценариев — от user‑based до service‑to‑service). Типичный сигнал «нужна авторизация» выглядит так: сервер отвечает HTTP 401 Unauthorized, после чего клиент инициирует авторизационный flow и начинает прикладывать access token в каждом HTTP‑запросе.

Важно то, что этот блок относится именно к HTTP‑based транспорту. Для stdio обычно работают другие модели (например, секреты/учётки из окружения), потому что канал локальный и контролируется хостом.


Почему именно MCP

Чтобы ответить на данный вопрос надо прежде всего разобраться, а почему не нашлось подходящего из существующих решений до появления MCP. Связано это с тем, что интеграции между LLM‑приложениями и внешними системами чаще строились как набор точечных, вендорных или продуктовых решений: каждый хост/модель/платформа имели свой формат tool calling, свои плагины или свой слой коннекторов. Это работало, но плохо масштабировалось: один и тот же источник данных или действие приходилось переупаковывать под разные экосистемы.

И как обычно это бывает, для устранения существующих минусов придумывают универсальное решение. Именно так и поступила компания Anthropic, которая представила MCP (анонс был 25 ноября 2024), который закрывает разрыв как универсальный и открытый протокол взаимодействия LLM‑приложений с внешними системами: не только tool calling, но и стандартизировано подключать контекст и capabilities серверов через единый клиент‑серверный контракт.


Итоги

Что мы зафиксировали:

  • MCP — это контракт между LLM-приложением и внешним миром, оформленный как JSON-RPC протокол с чётким lifecycle (initialize/initialized).

  • Основные server features раскладываются на: читать (resources), выполнять (tools) и шаблонизировать взаимодействия (prompts), плюс утилиты вроде completion.

  • Реализация упирается не только в методы протокола, но и в транспорт: stdio проще и безопаснее для локальных интеграций, Streamable HTTP — база для удалённых серверов, но требует безопасности и (часто) авторизации.

Что дальше логично разобрать отдельно:

  1. Client features (roots/sampling/elicitation) и реальные “server→client” флоу.

  2. Практика безопасности: human-in-the-loop, политика подтверждений, минимальные привилегии и аудит.

  3. Реальный пример end-to-end (сессия, листинг, вызов tool, подписки) и как это тестировать.

Если будет интерес — продолжу серией, но уже с упором на практику реализации и типовые ловушки продакшена.

На этом все. Спасибо за внимание! Надеюсь данный материал действительно был полезен для вас. В качестве основного источника знаний и примеров непосредственно использовалась официальная документация, которая находится здесь.