От нуля к единице: MCP и много другого на пути к его пониманию. ai.. ai. IT-стандарты.. ai. IT-стандарты. llm.. ai. IT-стандарты. llm. mcp.. ai. IT-стандарты. llm. mcp. mcp-server.. ai. IT-стандарты. llm. mcp. mcp-server. model context protocol.. ai. IT-стандарты. llm. mcp. mcp-server. model context protocol. network.. ai. IT-стандарты. llm. mcp. mcp-server. model context protocol. network. rpc.. ai. IT-стандарты. llm. mcp. mcp-server. model context protocol. network. rpc. агенты.. ai. IT-стандарты. llm. mcp. mcp-server. model context protocol. network. rpc. агенты. искусственный интеллект.. ai. IT-стандарты. llm. mcp. mcp-server. model context protocol. network. rpc. агенты. искусственный интеллект. Сетевые технологии.

Сегодня ядром данной статьи будет 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}
}
От нуля к единице: MCP и много другого на пути к его пониманию - 1

А 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" }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 2

Как это связано с MCP? А в MCP операции вида resources/list, resources/read, resources/templates/list, completion/complete и тому подобное — это JSON‑RPC методы (та самая строка method, а параметры уходят в 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 сам правильно «склеивает» ответ с нужным запросом.


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

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

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

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

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

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

Рисунок 1.

Рисунок 1.

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

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

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

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

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

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

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

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

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

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

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

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

Рисунок 2.

Рисунок 2.

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

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

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

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

Список доступных ресурсов можно получить через обращение JSON‑RPC метод "resources/list" и он поддерживает cursor‑pagination (cursornextCursor). Про 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"
  }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 5
{
  "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"
  }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 6

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"
  }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 7
{
  "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}"
      }
    ]
  }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 8

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"
  }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 9
{
  "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 и много другого на пути к его пониманию - 10

Чтобы подставлять валидные аргументы в шаблоны в 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"
    } 
  }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 11
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "completion": {
      "values": ["src/main.rs", "src/main_test.rs"],
      "total": 10,
      "hasMore": true
    }
  }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 12

Другие сущности (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"
  }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 13
{
  "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"
  }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 14
  • аналогично 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')"
    }
  }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 15
{
  "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')"
        }
      }
    ]
  }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 16
  • 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"
  }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 17
{
  "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"
  }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 18
  • использование идет через tools/call(name, arguments) — сервер выполняет операцию и возвращает результат как набор content‑блоков (текст, изображение, аудио, ссылки на ресурсы и так далее). «поток-4»

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {
      "location": "New York"
    }
  }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 19
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Current weather in New York:n Temperature: 72°Fn Conditions: Partly cloudy"
      }
    ],
    "isError": false
  }
}
От нуля к единице: MCP и много другого на пути к его пониманию - 20
  • 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 }

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

Рисунок 3.

Рисунок 3.

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

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

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


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

Послесловие. Несмотря на то, что, как мне кажется, здесь было разобрано достаточно много, это довольно небольшая часть всего MCP (например, я не затрагивал client features: sampling, roots, elicitation), поэтому если статья зайдет, буду делать следующие части)

Автор: single_focus

Источник

Rambler's Top100