Tools и MCP · Лекция 3 ← На главную
Лекция 3

Tools и MCP

Как расширять возможности агента

Фокус: кастомные инструменты и протокол MCP

Представление

Аватар спикера
Добавьте фото по пути ../../assets/avatar.jpg

Кто выступает

  • ГТЛ в направлении СиВ
  • Процессный менеджер во Взыскании
  • ИИ Евангелист
  • Человек с веником в ...кхм голове (с) по мнению коллег

Формат: tools, MCP-протокол, интеграция агента с внешним миром.

Содержание

1. Два слоя расширения

Tools (действия) и MCP (протокол) — общая архитектура

2. Tools

Кастомные инструменты, tool calling, как написать свой tool

3. MCP

Протокол, серверы, подключение внешних сервисов

4. Tool vs MCP

Когда хватит tool, а когда нужен MCP — критерии выбора

5. CLI vs MCP

Когда CLI утилита лучше MCP-сервера

6. Превью

Следующая лекция: субагенты и оркестрация

Раздел 01

Два слоя расширения

Tools — действия агента. MCP — протокол связи с внешним миром.

Зачем агенту расширения

Без tools

  • Агент может только генерировать текст
  • Не видит файловую систему
  • Не запускает команды
  • Не обращается к API

По сути — чат-бот

С tools

  • Читает и пишет файлы
  • Запускает терминал
  • Ищет по кодовой базе
  • Взаимодействует с браузером, БД, API

Полноценный агент, действующий в реальном мире

Tools превращают генератор текста в агента, способного действовать.

Два способа дать агенту инструменты

Агент (LLM)

Видит единый список tools — не различает встроенные и MCP

Хост (IDE, CLI, ваш сервер)

Принимает tool_use, решает как выполнить

Локально

Read, Edit, Bash, кастомные

MCP Server(ы)

GitHub, Sentry, Playwright

Встроенные / кастомные

  • Хост выполняет сам, в своём процессе
  • Определены хостом или вами через API
  • Примеры: Read, Edit, Bash, Glob

MCP tools

  • Хост пересылает вызов MCP-серверу через JSON-RPC
  • Отдельный процесс, переиспользуемый между агентами
  • Примеры: GitHub MCP, Playwright MCP, Sentry MCP

Для модели — без разницы

name + description + input_schema. Модель не знает и не должна знать, как хост выполнит вызов.

Как агент вызывает tool

1. Задача

Пользователь даёт задачу

2. Think

LLM решает, какой tool нужен

3. Tool Call

Отправляет JSON с именем tool и параметрами

4. Result

Получает результат, решает: продолжить или завершить

{
  "type": "tool_use",
  "name": "Read",
  "input": {
    "file_path": "/src/app.ts",
    "limit": 50
  }
}

Агент не выполняет tool — он отправляет запрос. Хост (IDE, CLI) выполняет и возвращает результат.

Раздел 02

Tools

Встроенные инструменты, tool calling, как создать свой tool

Встроенные tools: что есть из коробки

Файловая система

  • Read — прочитать файл
  • Edit — точечная правка
  • Write — создать/перезаписать файл
  • Glob — найти файлы по паттерну
  • Grep — поиск по содержимому

Исполнение

  • Bash — терминальные команды
  • Agent — запуск субагентов
  • Notebook — ячейки Jupyter

Bash — самый мощный и самый опасный tool

Навигация

  • WebSearch — поиск в интернете
  • WebFetch — загрузка страниц
  • LSP — навигация по коду (go to definition, find references)

Набор зависит от хоста: CLI, VS Code, JetBrains

Встроенные vs кастомные tools

Встроенные

  • Уже определены хостом (IDE, CLI)
  • Вам не нужно их описывать
  • Read, Edit, Bash, Grep, WebSearch...

Доступны сразу при запуске агента

Кастомные

  • Вы определяете сами через Claude API
  • Передаёте в параметре tools каждого запроса
  • get_weather, db_query, check_endpoint...

Модель видит их точно так же, как встроенные

Дальше — как создать кастомный tool: описание, схема параметров, подключение к API.

Как LLM выбирает tool

Что видит модель

  • name — имя инструмента
  • description — текстовое описание: когда и зачем использовать
  • input_schema — JSON Schema входных параметров

Description — главный фактор выбора. Если описание плохое — tool не будет вызван. Tools занимают токены контекста!

Процесс выбора

  • LLM читает описания всех доступных tools
  • Сопоставляет с текущей задачей
  • Генерирует structured output: имя tool + параметры
  • Может вызвать несколько tools параллельно
{
  "name": "Read",
  "description": "Reads a file from the filesystem.
    Use this to read files before editing.
    Returns content with line numbers.",
  "input_schema": {
    "type": "object",
    "properties": {
      "file_path": {
        "type": "string",
        "description": "Absolute path to file"
      },
      "offset": {
        "type": "number",
        "description": "Start line number"
      },
      "limit": {
        "type": "number",
        "description": "Number of lines to read"
      }
    },
    "required": ["file_path"]
  }
}

Анатомия tool definition

{
  "name": "get_weather",
  "description": "Get current weather for a city.
    Use when user asks about weather
    or needs temperature data.",
  "input_schema": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "City name, e.g. Moscow"
      },
      "units": {
        "type": "string",
        "enum": ["celsius", "fahrenheit"],
        "description": "Temperature units"
      }
    },
    "required": ["city"]
  }
}

name

Короткое, понятное. Для кастомных — snake_case. Встроенные могут быть PascalCase (Read, Bash).

description

Самая важная часть. Должно быть ясно когда вызывать. Плохое описание = tool не используется.

input_schema

JSON Schema параметров. description у каждого поля помогает модели заполнить правильно.

required

Обязательные параметры. Всё остальное — optional с дефолтами.

Как написать хороший description

Плохо
{
  "name": "db_query",
  "description": "Queries the database"
}

Проблемы

  • Не ясно, когда использовать
  • Какую БД? Какие запросы?
  • Модель не знает ограничений
Хорошо
{
  "name": "db_query",
  "description": "Execute a read-only SQL
    query against the PostgreSQL
    analytics database. Use when
    user asks about metrics, user
    counts, or revenue data.
    Returns max 100 rows.
    Do NOT use for mutations."
}

Почему работает

  • Ясно: read-only, PostgreSQL, analytics
  • Когда: metrics, user counts, revenue
  • Ограничения: max 100, no mutations

Tool calling в Claude API

Запрос

import anthropic

client = anthropic.Anthropic()

response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    tools=[{
        "name": "get_weather",
        "description": "Get current weather",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string"}
            },
            "required": ["city"]
        }
    }],
    messages=[{
        "role": "user",
        "content": "Какая погода в Москве?"
    }]
)

Ответ модели

{
  "role": "assistant",
  "content": [
    {
      "type": "tool_use",
      "id": "toolu_01A...",
      "name": "get_weather",
      "input": {"city": "Moscow"}
    }
  ],
  "stop_reason": "tool_use"
}

Что дальше

  • Ваш код выполняет запрос к Weather API
  • Результат отправляется обратно как tool_result
  • Модель формирует финальный ответ

Полный цикл tool use

User

«Погода в Москве?»

LLM

tool_use: get_weather({city: "Moscow"})

Ваш код

Вызывает Weather API → получает данные

tool_result

{temp: -5, condition: "snow"}

LLM

«В Москве −5°C, идёт снег»

Модель никогда не вызывает API напрямую. Она просит ваш код сделать это и получает результат.

Замыкаем цикл: tool_result

Отправляем результат

# Получили tool_use от модели
tool_block = response.content[0]

# Выполняем действие
weather = call_weather_api(
    tool_block.input["city"]
)

# Отправляем результат обратно
final = client.messages.create(
    model="claude-sonnet-4-20250514",
    tools=tools,
    messages=[
        {"role": "user",
         "content": "Погода в Москве?"},
        {"role": "assistant",
         "content": response.content},
        {"role": "user",
         "content": [{
           "type": "tool_result",
           "tool_use_id": tool_block.id,
           "content": json.dumps(weather)
         }]}
    ]
)

Обработка ошибок

Если tool вернул ошибку — используйте is_error:

{
  "type": "tool_result",
  "tool_use_id": "toolu_01A...",
  "content": "City not found",
  "is_error": true
}

Модель увидит ошибку и скорректирует подход

tool_choice

  • "auto" — модель сама решает (по умолчанию)
  • "any" — обязана вызвать какой-либо tool
  • {"type": "tool", "name": "..."} — обязана вызвать конкретный

Полезно для structured extraction

Демо: создаём кастомный tool

Задача

Создать tool, который проверяет статус HTTP-эндпоинта и возвращает код ответа, время и заголовки.

  • Имя: check_endpoint
  • Параметры: url, method
  • Результат: status, latency, headers

План демо

  • Определяем tool definition (JSON Schema)
  • Пишем обработчик на Python
  • Подключаем к Claude API
  • Тестируем: «Проверь, работает ли api.example.com»
  • Показываем весь цикл: user → LLM → tool → result → ответ

Живое демо — создаём инструмент с нуля за 5 минут

Паттерны работы с tools

Параллельный вызов

  • Модель может запросить несколько tools одновременно
  • Хост выполняет параллельно
  • Результаты возвращаются пакетом

Значительно ускоряет I/O-bound операции

Цепочка вызовов

  • Результат одного tool → вход другого
  • Read файл → Edit файл → Bash (запуск тестов)
  • Модель сама строит цепочку

Это и есть agentic loop

Fallback

  • Если tool вернул ошибку — модель пробует другой подход
  • Grep не нашёл → Glob → Read
  • Bash failed → анализирует stderr → исправляет

Устойчивость = intelligence

Подводные камни (gotchas)

Размер tool_result

Tool вернул 100KB текста? Всё это попадает в контекст и уменьшает окно. Фильтруйте вывод, используйте limit/offset.

Стоимость agentic loop

Read → Edit → Bash → Read → Edit → Bash = 6+ API-вызовов. Десятки витков = ощутимые деньги. Используйте cache_control на tool definitions.

Галлюцинации tool calls

Модель может выдумать имя tool, неправильно заполнить параметры или вызвать tool без надобности. Защита: хорошие description + валидация на хосте.

Слишком много tools

50+ tools — модель теряется. Решение: dynamic tool selection, группировка, двухступенчатый выбор.

Раздел 03

MCP

Model Context Protocol — стандарт подключения агента к внешнему миру

Что такое MCP

Суть

  • Model Context Protocol — открытый стандарт от Anthropic
  • JSON-RPC 2.0 между агентом и внешними сервисами
  • Аналогия: USB для AI — один протокол, любое устройство
  • Поддержка: Claude Code, VS Code, Cursor, Roo Code, Zed...

Зачем

  • До MCP: каждая интеграция — уникальный код
  • С MCP: один сервер → работает во всех агентах
  • Сообщество: Тысячи готовых серверов
  • Безопасность: изоляция, permissions, audit

mcp.so, awesome-mcp-servers — каталоги

MCP решает проблему M×N интеграций. Вместо M агентов × N сервисов — один протокол для всех.

Архитектура MCP

Host

Claude Code, VS Code, Cursor

Client

MCP Client внутри хоста

Server

MCP Server (отдельный процесс)

Транспорт

  • stdio — сервер как дочерний процесс (stdin/stdout)
  • Streamable HTTP — основной HTTP-транспорт (JSON или SSE-поток)
  • SSE — deprecated, заменён на Streamable HTTP

stdio — для локальных серверов. Streamable HTTP — для удалённых

Capabilities сервера

  • Tools — функции, которые модель может вызвать
  • Resources — данные для чтения (с подписками на изменения)
  • Prompts — шаблоны промптов от сервера
  • Sampling — сервер запрашивает LLM через клиента

Жизненный цикл

  • Host запускает Server → initialize
  • Server отвечает capabilities
  • Host запрашивает список tools
  • Агент вызывает tools по необходимости
  • Сессия закрывается при завершении

MCP Server за 2 минуты (Python)

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("demo-server")

@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers together."""
    return a + b

@mcp.tool()
def get_time() -> str:
    """Get current server time."""
    from datetime import datetime
    return datetime.now().isoformat()

@mcp.resource("config://app")
def get_config() -> str:
    """Application configuration."""
    return "debug=true, version=1.0"

if __name__ == "__main__":
    mcp.run()

Что тут происходит

  • FastMCP — SDK для Python (pip install mcp)
  • @mcp.tool() — регистрация tool из обычной функции
  • Docstring → description для модели
  • Type hints → JSON Schema автоматически
  • @mcp.resource() — данные для чтения

Запуск

# Запуск
python server.py

# Или через MCP Inspector
npx @modelcontextprotocol/inspector \
  python server.py

Подключение MCP Server к агенту

Claude Code (.mcp.json)

{
  "mcpServers": {
    "demo": {
      "command": "python",
      "args": ["server.py"]
    },
    "github": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-github"
      ],
      "env": {
        "GITHUB_TOKEN": "ghp_..."
      }
    }
  }
}

VS Code / Cursor (.vscode/mcp.json)

{
  "servers": {
    "demo": {
      "type": "stdio",
      "command": "python",
      "args": ["server.py"]
    }
  }
}

После подключения

  • Tools сервера появляются в списке доступных
  • Модель видит их description
  • Вызывает как любой другой tool
  • Результат возвращается в контекст

Пользователь может не знать, что tool — внешний MCP

Популярные MCP-серверы

СерверЧто делаетПример использования
GitHubIssues, PRs, repos, code search«Создай PR с этими изменениями»
PlaywrightУправление браузером«Открой страницу и проверь, что кнопка работает»
SentryОшибки, performance«Покажи топ ошибок за сегодня»
PostgreSQLSQL-запросы к БД«Сколько пользователей зарегистрировалось вчера?»
SlackСообщения, каналы«Отправь результаты деплоя в #releases»
FilesystemБезопасный доступ к файламSandbox для файловых операций
Context7Документация библиотек«Покажи как использовать React Query v5»

Каталоги: mcp.so, smithery.ai, glama.ai — тысячи серверов. Большинство — npm или pip install.

Демо: подключаем MCP-сервер

Сценарий

  • Подключаем реальный MCP-сервер (Playwright)
  • Даём агенту задачу, требующую браузер
  • Наблюдаем, как агент решает использовать MCP-tools
  • Видим: открытие страницы, поиск элементов, скриншот

Что показываем

  • Конфигурация в .mcp.json
  • Список tools после подключения
  • Tool call от агента → действие в браузере
  • Результат возвращается в контекст
  • Агент продолжает работу с полученными данными

Живое демо — агент управляет браузером через MCP

MCP: ключевые концепции

Tools

  • Функции с side effects
  • Модель вызывает по необходимости
  • Основная capability для агентов

Tool = действие

Resources

  • Данные для чтения (read-only)
  • URI-адресация: file:///, db://, config://
  • Подписки: сервер уведомляет об изменениях

Resource = данные. Без side effects.

Prompts & Sampling

  • Prompts — серверные шаблоны: /review {pr}
  • Sampling — сервер запрашивает LLM-completions через клиента
  • Capability negotiation при initialize

Сервер декларирует, что умеет. Клиент выбирает подмножество.

Безопасность MCP

Риски

  • Tool poisoning — вредоносный сервер внедряет инструкции в description
  • Prompt injection через data — данные от сервера содержат инструкции для модели
  • Чрезмерные разрешения — сервер с доступом к БД может удалить данные
  • Утечка секретов — токены в env переменных сервера

Защита

  • Permissions — явное разрешение на каждый tool call
  • Аудит — логирование всех вызовов
  • Sandbox — сервер работает в изоляции
  • Проверка серверов — используйте только из надёжных источников
  • Принцип минимальных привилегий — read-only токены по умолчанию

MCP-сервер — это расширение агента. Доверяйте ему как npm-пакету: проверяйте перед установкой.

Раздел 04

Tool vs MCP

Когда хватит tool, а когда нужен MCP — критерии выбора

Сравнение: Tool vs MCP

Кастомный Tool (API)MCP Server
Где живётВнутри вашего кода (обработчик)Отдельный процесс/сервер
ПереиспользованиеТолько в вашем приложенииЛюбой MCP-совместимый агент
ПротоколВаш собственныйСтандартный JSON-RPC
ЯзыкЛюбой (обработчик отделён от API)Любой (Python, TS, Go, Rust...)
ИзоляцияВ процессе приложенияОтдельный процесс, sandbox
LatencyМинимальная (in-process)Overhead JSON-RPC через stdio/HTTP
СложностьНизкая (одна функция)Средняя (сервер + конфиг)
ЭкосистемаНетТысячи готовых серверов

Когда что выбрать

Хватит Tool

  • Логика специфична для вашего приложения
  • Нужен доступ к внутреннему состоянию (in-memory данные)
  • Одноразовый инструмент, не планируете переиспользовать
  • Простой API: одна функция, несколько параметров
  • Не хотите усложнять инфраструктуру

Пример: валидация бизнес-логики, расчёт скидки, внутренний поиск

Нужен MCP

  • Интеграция с внешним сервисом (GitHub, Jira, Slack)
  • Хотите переиспользовать в разных агентах/IDE
  • Нужна изоляция (sandbox, отдельные permissions)
  • Сложная интеграция с множеством tools
  • Есть готовый MCP-сервер в экосистеме

Пример: работа с GitHub, управление браузером, мониторинг

Правило: начните с tool. Если его хочется переиспользовать или изолировать — оберните в MCP.

Путь развития: от tool к MCP

1. Функция

Обычная функция в коде приложения

2. Tool

Подключаем к API как tool definition

3. MCP Server

Оборачиваем в MCP для переиспользования

4. Публикация

npm/pip publish → другие команды подключают

Аналогия

Функция → npm-пакет → REST API. Тот же путь, но для AI-инструментов.

Не прыгайте

Не нужно сразу писать MCP-сервер. YAGNI — начните с простого tool.

Но думайте вперёд

Если tool может быть полезен другим агентам — закладывайте чистый интерфейс.

Раздел 05

CLI vs MCP

Когда CLI утилита лучше MCP-сервера

Проблема: не всё нужно оборачивать

У многих инструментов уже есть отличные CLI-утилиты:

git / gh

Git-операции, PR, issues, actions

docker / kubectl

Контейнеры, оркестрация, деплой

curl / psql / npm

HTTP-запросы, БД, пакеты

Агент вызывает их через Bash tool напрямую. MCP-сервер — это дополнительный слой, который не всегда добавляет ценность.

5 причин выбрать CLI

1. CLI уже покрывает всё

gh pr list даёт тот же результат, что MCP GitHub-сервер. Зачем прослойка?

2. Больше гибкости

Pipe, фильтры, jq, комбинирование команд. MCP ограничен фиксированным набором tools.

3. Нет overhead

Не нужно запускать процесс, конфигурировать .mcp.json, ставить зависимости.

4. Лучше документация

git --help, man-страницы, Stack Overflow. MCP-серверы — часто минимум документации.

5. LLM уже знает CLI

Модель отлично владеет git, curl, docker из обучающих данных. С MCP-tool ей нужно разбираться по description.

CLI: мощь комбинирования

# Такое MCP-серверу GitHub не по силам:
gh pr list --json number,title,author \
  | jq '.[] | select(.author.login=="sazonov")'

# Комбинирование нескольких CLI:
git diff HEAD~3 --stat | grep ".ts" | wc -l

# Сложные запросы через API:
gh api repos/myorg/myrepo/actions/runs \
  --jq '.workflow_runs[:3] | .[].conclusion'

CLI + pipe + jq = бесконечная гибкость. MCP-сервер ограничен тем, что автор предусмотрел.

Когда MCP всё-таки лучше CLI

СитуацияПочему MCP лучше
OAuth/токеныMCP управляет сессией; CLI требует настройки на каждой машине
Нет CLI у сервисаSentry, Linear, Notion — MCP заменяет curl к REST API
БезопасностьMCP ограничивает набор операций; Bash — полный доступ к shell
Structured outputMCP возвращает JSON; CLI — текст, в котором модель может ошибиться
Нет доступа к shellВеб-агенты, ограниченные среды без Bash
Типизация и валидацияJSON Schema параметров vs свободная строка в Bash
Несколько IDE/агентовКонфиг один раз → работает в любом MCP-клиенте

Дерево решений

Есть хорошая CLI-утилита?
├── Да → Агент хорошо с ней справляется?
│         ├── Да → Используй CLI через Bash ✅
│         └── Нет (сложный вывод, auth) → Рассмотри MCP
└── Нет → Есть REST API?
            ├── Да → MCP-сервер оправдан ✅
            └── Нет → Напиши свой tool / MCP

Правило: если Bash("gh pr list") даёт тот же результат, что MCP GitHub-сервер — выбирай CLI. Не добавляй абстракцию ради абстракции.

Итоги

Ключевые выводы

Что забрать с собой из этой лекции

6 вещей, которые стоит запомнить

1

Tools = действия. Без tools агент — просто чат-бот. Tools дают ему возможность действовать в реальном мире.

2

Description решает всё. Модель выбирает tool по описанию. Плохое описание = tool не вызывается.

3

MCP = USB для AI. Один протокол подключает любой внешний сервис к любому агенту.

4

Tool → MCP — эволюция. Начинайте с простого tool. Если нужно переиспользование — оборачивайте в MCP.

5

CLI — не забывай. Если у инструмента есть хорошая CLI-утилита — MCP-сервер может быть лишним. Bash + gh/git/docker часто проще и мощнее.

6

Безопасность — приоритет. MCP-сервер — расширение агента. Проверяйте серверы, ограничивайте разрешения, логируйте вызовы.

Практическое задание

Задание 1: Tool check_endpoint

  • Напишите tool definition: url + method → status, latency, headers
  • Реализуйте обработчик на Python (httpx)
  • Подключите к Claude API (tool_use → tool_result → ответ)
  • Тест: «Проверь, работает ли httpbin.org/get»

Полный код — в конспекте лекции

Задание 2: MCP Filesystem

  • Добавьте server-filesystem в .mcp.json
  • Ограничьте доступ: только /tmp/sandbox
  • Задача агенту: «Создай report.md с текущей датой»
  • Проверьте: какие tool calls использовал агент?

Ожидание: агент использует write_file от MCP, не встроенный Write

Дальше

Лекция 4

Субагенты, оркестрация и Hooks — как научить агента делегировать работу

Остаёмся на связи

Фото автора канала

Telegram канал

QR код на Telegram канал SazonovMaybeTalks t.me/SazonovMaybeTalks

Сканируйте QR или переходите по ссылке, чтобы получить обновления по следующим материалам.