惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
About on SuperTechFans
GbyAI
GbyAI
I
InfoQ
C
Cisco Blogs
T
Tor Project blog
NISL@THU
NISL@THU
N
Netflix TechBlog - Medium
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
T
The Exploit Database - CXSecurity.com
H
Help Net Security
Cisco Talos Blog
Cisco Talos Blog
The Register - Security
The Register - Security
Microsoft Azure Blog
Microsoft Azure Blog
G
Google Developers Blog
A
Arctic Wolf
Stack Overflow Blog
Stack Overflow Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
P
Palo Alto Networks Blog
aimingoo的专栏
aimingoo的专栏
Microsoft Security Blog
Microsoft Security Blog
P
Privacy International News Feed
L
LINUX DO - 热门话题
Know Your Adversary
Know Your Adversary
L
LangChain Blog
AWS News Blog
AWS News Blog
Security Latest
Security Latest
T
Threat Research - Cisco Blogs
U
Unit 42
P
Privacy & Cybersecurity Law Blog
Spread Privacy
Spread Privacy
WordPress大学
WordPress大学
Latest news
Latest news
L
Lohrmann on Cybersecurity
小众软件
小众软件
阮一峰的网络日志
阮一峰的网络日志
Simon Willison's Weblog
Simon Willison's Weblog
雷峰网
雷峰网
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Y
Y Combinator Blog
S
SegmentFault 最新的问题
Recent Announcements
Recent Announcements
C
Cybersecurity and Infrastructure Security Agency CISA
Martin Fowler
Martin Fowler
S
Schneier on Security
C
CERT Recently Published Vulnerability Notes
B
Blog
Project Zero
Project Zero
Recorded Future
Recorded Future

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Model Context Protocol (MCP): как ИИ-агенты «разговаривают» с внешним миром
Dmitry_Barov · 2026-04-24 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение10 мин

Охват и читатели134

Туториал

Recovery Mode

Взаимодействие MCP и LLM

Взаимодействие MCP и LLM

Если вы читали или смотрели видео про 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):

«Зачем вам зонтик? На Мальдивах всегда прекрасная погода. Даже если промокнете — настроение это вам не испортит.»

Здесь не было вызвано никаких инструментов. ИИ решил, что знает ответ независимо от погоды.

Но! Модель могла бы поступить как в еврейском анекдоте — ответить вопросом на вопрос. Примерно так:

«Уважаемая программа, я отвечу пользователю, если вы:

  1. Вызовете ваш инструмент get_weather (где я уже подставила нужные даты, страну и город).

  2. Вернёте мне ответ от 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.

Транспорта для доставки сообщений два:

  1. stdio — стандартные потоки ввода-вывода (stdin, stdout, stderr). Используется, когда MCP-сервер и MCP-клиент установлены на одном компьютере.

  2. 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: file:///docs.md, postgres://db/users?id=1

Prompts

Переиспользуемые шаблоны

Пользователь / UI

Готовые сценарии для человека: «Ревью кода», «Планирование отпуска». Не для автоматизации.

💡 Важный нюанс: LLM умеет работать только с Tools. Ресурсы и промпты полезны для вашей программы, но LLM напрямую их не видит. Если хотите, чтобы ИИ сам решал, когда читать файл — оберните resources/read в Tool. Тогда модель сможет запросить его через tool_calls.

🌑 Тёмная сторона протокола (о чём молчат туториалы)

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

В таблице перечислены возможности, которые предоставляет MCP-клиент:

Команда

Что делает

Почему стоит напрячься

sampling/create

Сервер просит вашу LLM что-то сгенерировать

Может «съесть» ваши токены. Публичный сервер погоды может использовать вашу модель для саммаризации логов, а платить будете вы.

elicitation/request

Сервер запрашивает подтверждение или данные у пользователя

«Подтвердите перевод на счёт X» или «Введите номер карты».

logging/message

Сервер шлёт логи и прогресс выполнения

Похоже на режим thinking у LLM. Полезно для отладки и отображения пользователю прогресса выполнения (например, в процентах)

roots

Вы даёте серверу доступ к локальным папкам

file:///Users/Projects/.env? Да, при неправильной настройке MCP-сервер прочитает ваши секреты.

🔌 Итак, что такое MCP

MCP — это описание формата, протокол для обмена JSONами. MCP состоит всего из двух слоёв:

  1. ТранспортHTTP/HTTPS (по сети) или STDIO (локально, как subprocess).

  2. Данные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 (с бесплатной сертификацией) и полезные видео: