
Если вы читали или смотрели видео про MCP, то наверняка сталкивались с таким комментарием:
«Спасибо, ещё одна статья, из которой я ничего не понял».
Аналогии и пояснения вроде «MCP — это как USB Type-C» или «MCP — это Tools, Resources и Prompts» лично мне не добавляли понимания. Поэтому я решил подробно изучить данную технологию и написать статью, где будет понятно, достоверно и применимо. Без магии. Попутно я прошёл обучение у Anthropic (ссылки дам, сертификаты выдают, курсы бесплатные, правда на английском).
Я ставил себе цель ответить на вопросы:
Что такое MCP и как он связан с ИИ?
Как чат GPT (большая языковая модель, LLM) может вызвать какой-то инструмент (tool)?
Модель же языковая, т.е. она умеет говорить, рассуждать, отвечать, но никак не делать. Как LLM может читать файлы, вызывать программы, открывать интернет сайты, вызывать внешние API?
Для программистов, кто в теме и уже использовал MCP-сервера, т.е. знает серверные примитивы: tools, resources и prompts, возможно будут интересны клиентские примитивы: sampling, roots, elicitation. Они звучат загадочно и трудно переводимы. И чтобы вас заинтриговать: MCP-сервер благодаря sampling может «сжигать» ваши LLM-токены для выполнения своих серверных задач. А благодаря roots получать доступ к файлам на вашем компьютере.
Итак, поехали.
? Механика диалога: OpenAI-совместимый API в деталях
Люди общаются с чатами GPT (Qwen, Deepseek, GigaChat, Grok и другими) через чат-бот. А программы получаются доступ к таким моделям через API. Причем в индустрии уже есть стандарт, он называется OpenAI-совместимый API. Вы платите, получается ключ (длинную строку символов) и можете программно задавать вопросы и получать ответы от LLM в формате JSON.
? Лайфхак: как общаться бесплатно с разными моделями. Такую возможность предоставляет console.groq.com. Не спутайте: название сайта похоже на модель Grok от Илона Маска, но это другой сервис.
Но мы отвлеклись. Диалог с моделью через OpenAI-совместимый API выглядит примерно так:
? Сценарий: «Погода на Мальдивах»
Системный промпт (role: system):
«Ты создан для помощи людям в том, как им одеваться в путешествия в зависимости от погоды. В твоём распоряжении есть инструмент
get_weatherс параметрами: дата начала путешествия, дата конца, страна, населённый пункт.»
Пользователь спрашивает (role: user):
«Я еду завтра на Мальдивы. Брать ли зонтик?»
Модель могла бы ответить (role: assistant):
«Зачем вам зонтик? На Мальдивах всегда прекрасная погода. Даже если промокнете — настроение это вам не испортит.»
Здесь не было вызвано никаких инструментов. ИИ решил, что знает ответ независимо от погоды.
Но! Модель могла бы поступить как в еврейском анекдоте — ответить вопросом на вопрос. Примерно так:
«Уважаемая программа, я отвечу пользователю, если вы:
Вызовете ваш инструмент
get_weather(где я уже подставила нужные даты, страну и город).Вернёте мне ответ от
get_weatherв чат, тогда я соберу финальную рекомендацию.»
Вы уже заметили три стандартные роли: system, user, assistant. А знали ли вы, что в стандарте OpenAI API есть четвёртая роль — tool? Именно через неё MCP «сшивается» с диалогом модели.
? Шаг 1: Запрос от пользователя (вместе с системным промтом)
Ваша программа (назовём её Host) формирует запрос к модели. Обратите внимание: мы передаём не только вопрос пользователя, но и список доступных инструментов в поле tools(в данном случает единственный инструмент get_weather).
POST /v1/chat/completions { "model": "google/gemma-4-26b-a4b-it", "messages": [ { "role": "system", "content": "Ты подсказываешь людям как им одеваться в путешествие в зависимости от погоды. В твоем распоряжении есть инструмент get_weather с параметрами (дата начала путешествия, дата конца, страна, населенный пункт)." }, { "role": "user", "content": "Я еду завтра (1 мая 2026 года) на Мальдивы на недельку. Брать ли зонтик :-)" } ], "tools": [ { "type": "function", "function": { "name": "get_weather", "description": "Получает прогноз погоды для указанного города. Возвращает температуру, вероятность осадков, давление и силу ветра.", "parameters": { "type": "object", "properties": { "country": {"type": "string", "description": "Country name, e.g. Maldives"}, "city": {"type": "string", "description": "City name, e.g. Malé"}, "start_date": {"type": "string", "format": "date", "description": "Start date in YYYY-MM-DD"}, "end_date": {"type": "string", "format": "date", "description": "End date in YYYY-MM-DD"} }, "required": ["country", "city", "start_date", "end_date"] } } } ] }
? Обратите внимание:
tools— это не сам MCP-запрос. Это просто описание, которое вы «скармливаете» модели, чтобы она знала: «Эй, если нужно узнать погоду — вот как можно попросить».
? Ответ от LLM «вопросом на вопрос»
Модель понимает: у неё нет актуальных данных о погоде на Мальдивах. Она не выдумывает ответ. Вместо этого она делегирует задачу вам, т.е. просит вызвать get_weather с уже подставленными параметрами.
... начало пропущено ... { "message": { "role": "assistant", "content": null, "tool_calls": [ { "id": "chatcmpl-tool-abac20964c6ac906", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\": \"Malé\", \"country\": \"Maldives\", \"end_date\": \"2026-05-08\", \"start_date\": \"2026-05-01\"}" } } ] }, "finish_reason": "tool_calls" } ... конец пропущен ...
Полный текст JSON здесь:
{ "id": "chatcmpl-9e9710ae64b15c96", "object": "chat.completion", "created": 1777024800, "model": "google/gemma-4-26b-a4b-it", "choices": [ { "index": 0, "message": { "role": "assistant", "content": null, "tool_calls": [ { "id": "chatcmpl-tool-abac20964c6ac906", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\": \"Malé\", \"country\": \"Maldives\", \"end_date\": \"2026-05-08\", \"start_date\": \"2026-05-01\"}" } } ] }, "finish_reason": "tool_calls" } ], "usage": { "prompt_tokens": 275, "completion_tokens": 57, "total_tokens": 332 } }
Обратите внимание:
✅поле
content: null, т.е. модель не генерирует текст для отображения пользователю, а запрашивает инструмент. Или текст в полеcontent, или вызов инструментов в массивеtool_calls. Либо-либо, но не вместе.✅
finish_reason: "tool_calls"— это сигнал вашему коду: «Стоп. Не показывай ответ пользователю. Сначала выполни инструмент». (Все коды возврата, кромеfinish_reasonразберем ниже).✅
arguments— это строка, содержащая валидный JSON. ИИ мозг сформулировал готовые параметры и имя MCP-инструмента, в котором нуждается. Двойные кавычки\"уже экранированы. Одинарные кавычки здесь сломают парсинг.
? Это и есть интеллект. Не в выполнении запроса. А в понимании:
Что у модели нет данных «в голове»
Что есть инструмент
get_weather, который может помочьКакие параметры нужны (
country,city,start_date)Как извлечь их из вопроса пользователя («завтра» →
2026-05-01, «Мальдивы» →country: "Maldives")
Большая языковая модель не «вызывает» MCP-сервер напрямую. Она говорит вашей программе: «Эй, я не могу ответить, но знаю, кто может. Сходи, спроси вот так, а результат верни мне — я соберу финальный ответ».
? Шаг 2: Ваш код выполняет MCP-запрос (а не LLM-модель!)
Теперь в дело вступает ваш код (Host). Он видит finish_reason: "tool_calls" и делает:
# 1. Парсим вызов инструмента tool_call = response.choices[0].message.tool_calls[0] tool_name = tool_call.function.name # "get_weather" args = json.loads(tool_call.function.arguments) # {"country": "Maldives", ...} # 2. Вызываем MCP-сервер (это НЕ запрос к LLM!) result = await mcp_client.call_tool(name=tool_name, arguments=args) # 3. Получаем : # result = "Malé, Maldives: 31°C, 20% chance of rain"
⚠️ Важно: MCP-сервер в простейшем случае — это просто программа. Сервер не «думает», это не ИИ. Программа получила параметры, сделала запрос к погодному API (или кэшу), вернула строку с ответом. Всё.
MCP-клиент (ваша программа) общается с MCP-сервером посредством обмена JSON сообщениями по стандарту JSON-RPC 2.0.
Транспорта для доставки сообщений два:
stdio— стандартные потоки ввода-вывода (stdin,stdout,stderr). Используется, когда MCP-сервер и MCP-клиент установлены на одном компьютере.Streamable HTTP / HTTPS — универсальный способ для общения по сети.
Ответ от MCP-сервера приходит примерно в таком виде:
{ "jsonrpc": "2.0", "id": 42, "result": { "content": [ { "type": "text", "text": "Malé, Maldives: 31°C, 20% chance of rain, wind 12 km/h" } ], "isError": false } }
? Обратите внимание: внутри
content[].textлежит обычная строка. Именно её вы потом «упакуете» вrole: "tool"для отправки обратно в LLM. MCP-сервер не знает проassistant,userилиfinish_reason. Он просто отдаёт данные в своём формате.
? Шаг 3: Второй запрос к LLM (с результатом инструмента)
Теперь вы повторно отправляете полную историю диалога, включая результат выполнения инструмента. Обратите внимание на новую роль tool и обязательный tool_call_id:,
... Полный повтор беседы ... { "role": "tool", // <- Четвертая роль стандарта OpenAI API "tool_call_id": "chatcmpl-tool-abac20964c6ac906", // <- Связка с вопросом выше "content": "Malé, Maldives: 31°C, 20% chance of rain, wind 12 km/h" } ... Описание инструментов, которые модель имеет права использовать ...
Полный JSON без сокращений здесь:
POST /v1/chat/completions { "model": "google/gemma-4-26b-a4b-it", "messages": [ { "role": "system", "content": "Ты создан для помощи людям в том, как им одеваться в путешествия в зависимости от погоды. В твоем распоряжении есть инструмент get_weather с параметрами (дата начала путешествия, дата конца, страна, населенный пункт)." }, { "role": "user", "content": "Я еду завтра на Мальдивы. Брать ли зонтик?" }, { "role": "assistant", "content": null, "tool_calls": [ { "id": "chatcmpl-tool-abac20964c6ac906", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\": \"Malé\", \"country\": \"Maldives\", \"start_date\": \"2026-05-01\", \"end_date\": \"2026-05-08\"}" } } ] }, { "role": "tool", // <- Четвертая роль стандарта OpenAI API "tool_call_id": "chatcmpl-tool-abac20964c6ac906", // <- Связка с вопросом выше "content": "Malé, Maldives: 31°C, 20% chance of rain, wind 12 km/h" } ], "tools": [ { "type": "function", "function": { "name": "get_weather", "description": "Получает прогноз погоды для указанного города. Возвращает температуру, вероятность осадков, давление и силу ветра.", "parameters": { "type": "object", "properties": { "country": {"type": "string", "description": "Country name, e.g. Maldives"}, "city": {"type": "string", "description": "City name, e.g. Malé"}, "start_date": {"type": "string", "format": "date", "description": "Start date in YYYY-MM-DD"}, "end_date": {"type": "string", "format": "date", "description": "End date in YYYY-MM-DD"} }, "required": ["country", "city", "start_date", "end_date"] } } } ] }
Почему передаем в role: "tool"и зачем tool_call_id ?
✅ Чтобы модель понимала: это не новый вопрос от человека, а ответ на её же запрос
✅ Чтобы связать результат с конкретным вызовом через
tool_call_id✅ Чтобы при параллельных вызовах (3 инструмента сразу) модель не запуталась, какой результат к какому запросу относится
? Финальный ответ от LLM
Теперь у модели есть всё: вопрос пользователя + актуальные данные о погоде. Она генерирует человеческий ответ:
{ "choices": [{ "message": { "role": "assistant", "content": "На Мальдивах завтра 31°C, вероятность дождя 20%. Зонтик можно не брать, но лёгкий дождевик не помешает. Хорошего отдыха! ☀️", "tool_calls": null }, "finish_reason": "stop" }] }
Полный JSON итогового ответа от LLM здесь:
{ "id": "chatcmpl-906e5202d20eeb3d", "object": "chat.completion", "created": 1777026960, "model": "google/gemma-4-26b-a4b-it", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "На Мальдивах завтра 31°C, вероятность дождя 20%. Зонтик можно не брать, но лёгкий дождевик не помешает. Хорошего отдыха! ☀️", "tool_calls": [] }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 350, "completion_tokens": 94, "total_tokens": 444 } }
Пояснения:
✅
finish_reason: "stop"это сигнал: модель закончила, можно показывать ответ пользователю.✅
contentсодержит финальный, человеческий текст.-
✅
tool_calls: [] или null— больше инструментов не нужно.? Все стандартные значения finish_reason в OpenAI-совместимом API
Значение
Когда появляется
stopМодель "штатно" сгенерировала завершающий токен. Можно "курсор" для ввода следующего вопроса показывать пользователю
tool_callsМодель сгенерировала требования, чтобы ей вызвали один или несколько инструментов и написали ответы
lengthДостигнут лимит
max_tokensили общий лимит контекстаcontent_filterСработала система модерации (запрещённый контент, токсичность и т.п.)
? Примитивы MCP-сервера (Tools, Resources, Prompts)
Я подробно описал как работает Tools. Статья уже получается длинной, поэтому только упомяну два других примитива.
Примитив |
Аналогия в программировании |
Кто владелец |
Зачем нужен |
|---|---|---|---|
Tools |
Процедуры/Функции (Write + Read) |
Модель решает, когда вызвать |
Действия: запрос к БД, отправка email, расчёт. Могут менять состояние. |
Resources |
Read-only данные |
Приложение (Host) подкидывает в контекст |
Файлы, конфиги, записи БД. Доступ по URI: |
Prompts |
Переиспользуемые шаблоны |
Пользователь / UI |
Готовые сценарии для человека: «Ревью кода», «Планирование отпуска». Не для автоматизации. |
? Важный нюанс: LLM умеет работать только с Tools. Ресурсы и промпты полезны для вашей программы, но LLM напрямую их не видит. Если хотите, чтобы ИИ сам решал, когда читать файл — оберните
resources/readв Tool. Тогда модель сможет запросить его черезtool_calls.
? Тёмная сторона протокола (о чём молчат туториалы)
MCP — двусторонний протокол. Сервер может не только отдавать данные, но и запрашивать ресурсы у вашего клиента.
В таблице перечислены возможности, которые предоставляет MCP-клиент:
Команда |
Что делает |
Почему стоит напрячься |
|---|---|---|
|
Сервер просит вашу LLM что-то сгенерировать |
Может «съесть» ваши токены. Публичный сервер погоды может использовать вашу модель для саммаризации логов, а платить будете вы. |
|
Сервер запрашивает подтверждение или данные у пользователя |
«Подтвердите перевод на счёт X» или «Введите номер карты». |
|
Сервер шлёт логи и прогресс выполнения |
Похоже на режим |
|
Вы даёте серверу доступ к локальным папкам |
|
? Итак, что такое MCP
MCP — это описание формата, протокол для обмена JSONами. MCP состоит всего из двух слоёв:
Транспорт —
HTTP/HTTPS(по сети) илиSTDIO(локально, как subprocess).Данные —
JSON-RPC 2.0. Строгая договорённость о том, как упаковывать запросы, ответы и уведомления. В статье я практически это не затронул.
Какую проблему это решает?
До MCP каждый разработчик ИИ-агента писал свой велосипед: парсил Swagger, хардкодил эндпоинты, вручную валидировал параметры.
MCP заменяет это само-описанием. Вместо чтения документации ИИ-агент (MCP-клиент) просто спрашивает у MCP-сервера: {"method": "tools/list"}. И получает список инструментов и их параметров. Причем ответ в виде JSON, он понятен и человеку и модели.
Таким образом:
? LLM — это мозг. Понимает задачу, принимает решение, когда и какой инструмент вызвать и с какими параметрами.
✋ Ваш код (Host) — это руки. Выполняет реальные запросы к MCP-серверам, получает данные, возвращает их модели.
⚙️ MCP-сервер — это программа. Скрипт, API, БД, файловая система и т.д. Он не AI. Он просто ждёт вызова и отдаёт результат.
MCP — это стандартизированный способ для вашего кода получать инструменты и данные, чтобы «кормить» ими LLM через обычный OpenAI-совместимый API.
Никакой магии. LLM не вызывает серверы. Серверы не думают. Ваш код — единственный мост.
finish_reason: "tool_calls"— сигнал делегации.role: "tool"— канал возврата данных.content: null— правило чистого разделения.
Продолжение следует ...
Спасибо, что дочитали. Уверен вы смогли прояснить для себя что же такое MCP.
Если по комментариям будет виден интерес в подробном пояснении:
Перечня команд MCP (
tools/list,resource/get, ... )Форматов MCP-сообщений (
jsonrpc,params,result)Других примитивов, кроме
tools(resources,prompts,sampling)Как написать свой MCP-сервер или MCP-клиент на Python.
Как отлаживать с помощью
mcp inspector(почти аналог Postman и Swagger, но для MCP)
, то я с удовольствием напишу следующую часть статьи по вашим интересам.
Подписывайтесь на телеграмм @APIMAGIC — буду публиковать обновления и отвечать на вопросы.
P.S. Обещанные ссылки на курс MCP от Anthropic (с бесплатной сертификацией) и полезные видео:
Комментарии (6)

dmbozz
24.04.2026 16:56Всё хорошо объяснили, благодарю. Когда кому-то объясняешь, начинаешь и сам лучше понимать, это да.:)
Интересен вариант коллекции тулзов::
Допустим, имею несколько разных tool-ов - один, например, как у Вас - найдет погоду и температуру на Мальдивах. Второй - умеет искать среднюю цену на путевку в заданное время. А третий по команде "отформатируй флешку и запиши туда результаты поиска" умеет вызвать системную команду "format D:" с последующей записью на нее.
Как LLM поймет, что из них и в каком порядке вызвать в ответ на мой промт "собери на флешку 5 самых дешевых вариантов поездки на Мальдивы, и чтоб без дождей, пожалуйста" ?Верно ли понимаю, что description - как раз для этого ? чтобы LLM догадалась куда и на каком этапе бежать. И достаточно ли его, или существуют какие-то более тонкие настройки?

Dmitry_Barovik Автор
24.04.2026 16:56Спасибо на добром слове!
Да, у tools есть поле description. Да, LLM-ка читает описания и на их основе понимает, что умеет делать инструмент. Ну и в обычным промпте (неважно системном или пользовательском) можно дать пояснения: для таких-то целей и в таких-то ситуациях используй такие-то инструменты.
Например, вот часть реального системного промпта чата GPT 4.5:
https://github.com/asgeirtj/system_prompts_leaks/blob/main/OpenAI/GPT-4.5.md
Там, например видно:Tools
bio
И описание для чего применять.
Вообще вот здесь много честно слитых программистами системных промптов всемирно известных инструментов: https://github.com/asgeirtj/system_prompts_leaks

uvelichitel
24.04.2026 16:56Но! Модель могла бы поступить как в еврейском анекдоте — ответить вопросом на вопрос.
Кому нибудь модель задавала вопросы? Мне вот нет...
С какой стати модель доверяет тулзам? Достоверность источника(тулзы) чем нибудь подтверждается? Может хоть сертификаты какие)))
Dmitry_Barovik Автор
24.04.2026 16:56Нуу, физически вы как разработчик даёте либо не даёте доступ к MCP серверу. Доступ дали. MCP-сервер взломали. И вуаля, вместо , например, web -поиска или калькулятора код по просьбе LLM вызовет что-то очеееень неожиданное.

Dmitry_Barovik Автор
24.04.2026 16:56Кстати. Первый инструмент, который прописан в Claude Code так и называется: "Задай пользователю вопросы". А второй в списке инструмент: "Вызов командной строки".
https://github.com/asgeirtj/system_prompts_leaks/blob/main/Anthropic/claude-code.mdTools
AskUserQuestion
Use this tool when you need to ask the user questions during execution. This allows you to...:
Bash
Executes a given bash command with optional timeout...
Dmitry_Barovik Автор
Спасибо, еще одна статья, из которой я ничего не понял :P