- BrainTools - https://www.braintools.ru -
Если вы читали или смотрели видео про MCP, то наверняка сталкивались с таким комментарием:
«Спасибо, ещё одна статья, из которой я ничего не понял».
Аналогии и пояснения вроде «MCP — это как USB Type-C» или «MCP — это Tools, Resources и Prompts» лично мне не добавляли понимания. Поэтому я решил подробно изучить данную технологию и написать статью, где будет понятно, достоверно и применимо. Без магии. Попутно я прошёл обучение [1] у Anthropic (ссылки дам, сертификаты выдают, курсы бесплатные, правда на английском).
Я ставил себе цель ответить на вопросы:
Что такое MCP и как он связан с ИИ?
Как чат GPT (большая языковая модель, LLM) может вызвать какой-то инструмент (tool)?
Модель же языковая, т.е. она умеет говорить, рассуждать, отвечать, но никак не делать. Как LLM может читать файлы, вызывать программы, открывать интернет сайты, вызывать внешние API?
Для программистов, кто в теме и уже использовал MCP-сервера, т.е. знает серверные примитивы: tools, resources и prompts, возможно будут интересны клиентские примитивы: sampling, roots, elicitation. Они звучат загадочно и трудно переводимы. И чтобы вас заинтриговать: MCP-сервер благодаря sampling может «сжигать» ваши LLM-токены для выполнения своих серверных задач. А благодаря roots получать доступ к файлам на вашем компьютере.
Итак, поехали.
Люди общаются с чатами GPT (Qwen, Deepseek, GigaChat, Grok и другими) через чат-бот. А программы получаются доступ к таким моделям через API. Причем в индустрии уже есть стандарт, он называется OpenAI-совместимый API. Вы платите, получается ключ (длинную строку символов) и можете программно задавать вопросы и получать ответы от LLM в формате JSON.
💡 Лайфхак: как общаться бесплатно с разными моделями. Такую возможность предоставляет console.groq.com [2]. Не спутайте: название сайта похоже на модель Grok от Илона Маска, но это другой сервис.
Но мы отвлеклись. Диалог с моделью через OpenAI-совместимый API выглядит примерно так:
Системный промпт (role: system):
«Ты создан для помощи людям в том, как им одеваться в путешествия в зависимости от погоды. В твоём распоряжении есть инструмент
get_weatherс параметрами: дата начала путешествия, дата конца, страна, населённый пункт.»
Пользователь спрашивает (role: user):
«Я еду завтра на Мальдивы. Брать ли зонтик?»
Модель могла бы ответить (role: assistant):
«Зачем вам зонтик? На Мальдивах всегда прекрасная погода. Даже если промокнете — настроение это вам не испортит.»
Здесь не было вызвано никаких инструментов. ИИ решил, что знает ответ независимо от погоды.
Но! Модель могла бы поступить как в еврейском анекдоте — ответить вопросом на вопрос. Примерно так:
«Уважаемая программа, я отвечу пользователю, если вы:
Вызовете ваш инструмент
get_weather(где я уже подставила нужные даты, страну и город).Вернёте мне ответ от
get_weatherв чат, тогда я соберу финальную рекомендацию.»
Вы уже заметили три стандартные роли: system, user, assistant. А знали ли вы, что в стандарте OpenAI API есть четвёртая роль — tool? Именно через неё MCP «сшивается» с диалогом модели.
🔹 Шаг 1: Запрос от пользователя (вместе с системным промтом)
Ваша программа (назовём её Host) формирует запрос к модели. Обратите внимание [3]: мы передаём не только вопрос пользователя, но и список доступных инструментов в поле 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"
}
... конец пропущен ...
{
"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. ИИ мозг [4] сформулировал готовые параметры и имя MCP-инструмента, в котором нуждается. Двойные кавычки " уже экранированы. Одинарные кавычки здесь сломают парсинг.
🎭 Это и есть интеллект [5]. Не в выполнении запроса. А в понимании:
Что у модели нет данных «в голове»
Что есть инструмент
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"
}
... Описание инструментов, которые модель имеет права использовать ...
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"
}]
}
{
"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 — больше инструментов не нужно.
|
Значение |
Когда появляется |
|---|---|
|
|
Модель “штатно” сгенерировала завершающий токен. Можно “курсор” для ввода следующего ответа показывать пользователю |
|
|
Модель сгенерировала требования, чтобы ей вызвали один или несколько инструментов и написали ответы |
|
|
Достигнут лимит |
|
|
Сработала система модерации (запрещённый контент, токсичность и т.п.) |
Я подробно описал как работает 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 — это описание формата, протокол для обмена 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.
Если по комментариям будет виден интерес [6] в подробном пояснении:
Перечня команд MCP (tools/list, resource/get, … )
Форматов MCP-сообщений (jsonrpc, params, result)
Других примитивов, кроме tools (resources, prompts, sampling)
Как написать свой MCP-сервер или MCP-клиент на Python.
Как отлаживать с помощью mcp inspector (почти аналог Postman и Swagger, но для MCP)
, то я с удовольствием напишу следующую часть статьи по вашим интересам.
Подписывайтесь на телеграмм @APIMAGIC — буду публиковать обновления и отвечать на вопросы.
Автор: Dmitry_Barovik
Источник [10]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/29344
URLs in this post:
[1] обучение: http://www.braintools.ru/article/5125
[2] console.groq.com: http://console.groq.com
[3] внимание: http://www.braintools.ru/article/7595
[4] мозг: http://www.braintools.ru/parts-of-the-brain
[5] интеллект: http://www.braintools.ru/article/7605
[6] интерес: http://www.braintools.ru/article/4220
[7] Introduction to Model Context Protocol: https://anthropic.skilljar.com/introduction-to-model-context-protocol
[8] Model Context Protocol: Advanced Topics: https://anthropic.skilljar.com/model-context-protocol-advanced-topics
[9] Video: MCP Deep Dive (YouTube): https://www.youtube.com/watch?v=7j1t3UZA1TY
[10] Источник: https://habr.com/ru/articles/1027508/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1027508
Нажмите здесь для печати.