Как расширять возможности агента
../../assets/avatar.jpgФормат: tools, MCP-протокол, интеграция агента с внешним миром.
Tools (действия) и MCP (протокол) — общая архитектура
Кастомные инструменты, tool calling, как написать свой tool
Протокол, серверы, подключение внешних сервисов
Когда хватит tool, а когда нужен MCP — критерии выбора
Когда CLI утилита лучше MCP-сервера
Следующая лекция: субагенты и оркестрация
Tools — действия агента. MCP — протокол связи с внешним миром.
По сути — чат-бот
Полноценный агент, действующий в реальном мире
Tools превращают генератор текста в агента, способного действовать.
Видит единый список tools — не различает встроенные и MCP
Принимает tool_use, решает как выполнить
Read, Edit, Bash, кастомные
GitHub, Sentry, Playwright
name + description + input_schema. Модель не знает и не должна знать, как хост выполнит вызов.
Пользователь даёт задачу
LLM решает, какой tool нужен
Отправляет JSON с именем tool и параметрами
Получает результат, решает: продолжить или завершить
{
"type": "tool_use",
"name": "Read",
"input": {
"file_path": "/src/app.ts",
"limit": 50
}
}
Агент не выполняет tool — он отправляет запрос. Хост (IDE, CLI) выполняет и возвращает результат.
Встроенные инструменты, tool calling, как создать свой tool
Bash — самый мощный и самый опасный tool
Набор зависит от хоста: CLI, VS Code, JetBrains
Доступны сразу при запуске агента
tools каждого запросаМодель видит их точно так же, как встроенные
Дальше — как создать кастомный tool: описание, схема параметров, подключение к API.
Description — главный фактор выбора. Если описание плохое — 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"]
}
}
{
"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"]
}
}
Короткое, понятное. Для кастомных — snake_case. Встроенные могут быть PascalCase (Read, Bash).
Самая важная часть. Должно быть ясно когда вызывать. Плохое описание = tool не используется.
JSON Schema параметров. description у каждого поля помогает модели заполнить правильно.
Обязательные параметры. Всё остальное — optional с дефолтами.
{
"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."
}
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"
}
tool_result«Погода в Москве?»
tool_use: get_weather({city: "Moscow"})
Вызывает Weather API → получает данные
{temp: -5, condition: "snow"}
«В Москве −5°C, идёт снег»
Модель никогда не вызывает API напрямую. Она просит ваш код сделать это и получает результат.
# Получили 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
}
Модель увидит ошибку и скорректирует подход
"auto" — модель сама решает (по умолчанию)"any" — обязана вызвать какой-либо tool{"type": "tool", "name": "..."} — обязана вызвать конкретныйПолезно для structured extraction
Создать tool, который проверяет статус HTTP-эндпоинта и возвращает код ответа, время и заголовки.
check_endpointurl, methodЖивое демо — создаём инструмент с нуля за 5 минут
Значительно ускоряет I/O-bound операции
Это и есть agentic loop
Устойчивость = intelligence
Tool вернул 100KB текста? Всё это попадает в контекст и уменьшает окно. Фильтруйте вывод, используйте limit/offset.
Read → Edit → Bash → Read → Edit → Bash = 6+ API-вызовов. Десятки витков = ощутимые деньги. Используйте cache_control на tool definitions.
Модель может выдумать имя tool, неправильно заполнить параметры или вызвать tool без надобности. Защита: хорошие description + валидация на хосте.
50+ tools — модель теряется. Решение: dynamic tool selection, группировка, двухступенчатый выбор.
Model Context Protocol — стандарт подключения агента к внешнему миру
mcp.so, awesome-mcp-servers — каталоги
MCP решает проблему M×N интеграций. Вместо M агентов × N сервисов — один протокол для всех.
Claude Code, VS Code, Cursor
MCP Client внутри хоста
MCP Server (отдельный процесс)
stdio — для локальных серверов. Streamable HTTP — для удалённых
initializefrom 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 из обычной функции@mcp.resource() — данные для чтения# Запуск
python server.py
# Или через MCP Inspector
npx @modelcontextprotocol/inspector \
python server.py
{
"mcpServers": {
"demo": {
"command": "python",
"args": ["server.py"]
},
"github": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-github"
],
"env": {
"GITHUB_TOKEN": "ghp_..."
}
}
}
}
{
"servers": {
"demo": {
"type": "stdio",
"command": "python",
"args": ["server.py"]
}
}
}
Пользователь может не знать, что tool — внешний MCP
| Сервер | Что делает | Пример использования |
|---|---|---|
| GitHub | Issues, PRs, repos, code search | «Создай PR с этими изменениями» |
| Playwright | Управление браузером | «Открой страницу и проверь, что кнопка работает» |
| Sentry | Ошибки, performance | «Покажи топ ошибок за сегодня» |
| PostgreSQL | SQL-запросы к БД | «Сколько пользователей зарегистрировалось вчера?» |
| Slack | Сообщения, каналы | «Отправь результаты деплоя в #releases» |
| Filesystem | Безопасный доступ к файлам | Sandbox для файловых операций |
| Context7 | Документация библиотек | «Покажи как использовать React Query v5» |
Каталоги: mcp.so, smithery.ai, glama.ai — тысячи серверов. Большинство — npm или pip install.
.mcp.jsonЖивое демо — агент управляет браузером через MCP
Tool = действие
file:///, db://, config://Resource = данные. Без side effects.
/review {pr}Сервер декларирует, что умеет. Клиент выбирает подмножество.
MCP-сервер — это расширение агента. Доверяйте ему как npm-пакету: проверяйте перед установкой.
Когда хватит tool, а когда нужен MCP — критерии выбора
| Кастомный Tool (API) | MCP Server | |
|---|---|---|
| Где живёт | Внутри вашего кода (обработчик) | Отдельный процесс/сервер |
| Переиспользование | Только в вашем приложении | Любой MCP-совместимый агент |
| Протокол | Ваш собственный | Стандартный JSON-RPC |
| Язык | Любой (обработчик отделён от API) | Любой (Python, TS, Go, Rust...) |
| Изоляция | В процессе приложения | Отдельный процесс, sandbox |
| Latency | Минимальная (in-process) | Overhead JSON-RPC через stdio/HTTP |
| Сложность | Низкая (одна функция) | Средняя (сервер + конфиг) |
| Экосистема | Нет | Тысячи готовых серверов |
Пример: валидация бизнес-логики, расчёт скидки, внутренний поиск
Пример: работа с GitHub, управление браузером, мониторинг
Правило: начните с tool. Если его хочется переиспользовать или изолировать — оберните в MCP.
Обычная функция в коде приложения
Подключаем к API как tool definition
Оборачиваем в MCP для переиспользования
npm/pip publish → другие команды подключают
Функция → npm-пакет → REST API. Тот же путь, но для AI-инструментов.
Не нужно сразу писать MCP-сервер. YAGNI — начните с простого tool.
Если tool может быть полезен другим агентам — закладывайте чистый интерфейс.
Когда CLI утилита лучше MCP-сервера
У многих инструментов уже есть отличные CLI-утилиты:
Git-операции, PR, issues, actions
Контейнеры, оркестрация, деплой
HTTP-запросы, БД, пакеты
Агент вызывает их через
Bashtool напрямую. MCP-сервер — это дополнительный слой, который не всегда добавляет ценность.
gh pr list даёт тот же результат, что MCP GitHub-сервер. Зачем прослойка?
Pipe, фильтры, jq, комбинирование команд. MCP ограничен фиксированным набором tools.
Не нужно запускать процесс, конфигурировать .mcp.json, ставить зависимости.
git --help, man-страницы, Stack Overflow. MCP-серверы — часто минимум документации.
Модель отлично владеет git, curl, docker из обучающих данных. С MCP-tool ей нужно разбираться по description.
# Такое 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 лучше |
|---|---|
| OAuth/токены | MCP управляет сессией; CLI требует настройки на каждой машине |
| Нет CLI у сервиса | Sentry, Linear, Notion — MCP заменяет curl к REST API |
| Безопасность | MCP ограничивает набор операций; Bash — полный доступ к shell |
| Structured output | MCP возвращает 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. Не добавляй абстракцию ради абстракции.
Что забрать с собой из этой лекции
Tools = действия. Без tools агент — просто чат-бот. Tools дают ему возможность действовать в реальном мире.
Description решает всё. Модель выбирает tool по описанию. Плохое описание = tool не вызывается.
MCP = USB для AI. Один протокол подключает любой внешний сервис к любому агенту.
Tool → MCP — эволюция. Начинайте с простого tool. Если нужно переиспользование — оборачивайте в MCP.
CLI — не забывай. Если у инструмента есть хорошая CLI-утилита — MCP-сервер может быть лишним. Bash + gh/git/docker часто проще и мощнее.
Безопасность — приоритет. MCP-сервер — расширение агента. Проверяйте серверы, ограничивайте разрешения, логируйте вызовы.
check_endpointhttpx)Полный код — в конспекте лекции
server-filesystem в .mcp.json/tmp/sandboxОжидание: агент использует write_file от MCP, не встроенный Write
Субагенты, оркестрация и Hooks — как научить агента делегировать работу
Сканируйте QR или переходите по ссылке, чтобы получить обновления по следующим материалам.