Tool use, MCP и потоковая передача в API
В предыдущей статье мы разобрали базовый Messages API — как отправлять сообщения, читать ответы и считать токены. Теперь переходим к трём механизмам, которые превращают простой клиент в агента: инструменты (tool use), подключение MCP-серверов напрямую из API и стриминг.
Клиентские инструменты: описание через JSON Schema
Инструмент — это функция вашего кода, которую модель может вызвать. Описывается в параметре tools запроса тремя обязательными полями:
{
"name": "get_file_contents",
"description": "Читает содержимое текстового файла по заданному пути. Использовать, когда нужно получить исходный код, конфиг или данные из файловой системы. Возвращает строку. Не работает с бинарными файлами и URL.",
"input_schema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Абсолютный или относительный путь к файлу"
}
},
"required": ["path"]
}
}Самое важное поле — description. Именно по нему модель решает, когда и как использовать инструмент. Пишите конкретно: что делает, при каких запросах вызывать, что возвращает, чего не умеет. Два слова хуже четырёх предложений.
Для инструментов со сложными параметрами или форматными ограничениями добавьте input_examples — массив примерных аргументов. Каждый пример должен соответствовать input_schema — невалидный пример API отклонит ошибкой 400.
Цикл tool use: запрос → выполнение → результат
Когда модель решает вызвать инструмент, ответ приходит со stop_reason: "tool_use". В content — блок tool_use:
{
"type": "tool_use",
"id": "toolu_01AbCdEf",
"name": "get_file_contents",
"input": {"path": "src/main.py"}
}Три поля: id (нужен для tool_result), name (какую функцию запустить), input (аргументы). Ваш код выполняет операцию, затем отправляет результат в новом user-сообщении:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01AbCdEf",
"content": "import anthropic\n\ndef main():\n ..."
}
]
}Перед этим — обязательно дописать ассистентский ответ с блоком tool_use в историю messages. API stateless, состояние полностью в ваших руках. Если инструмент завершился ошибкой — передайте "is_error": true с текстом ошибки в content, и модель скорректирует подход.
> Важно: в user-сообщении с результатами блоки tool_result должны идти первыми — до любого текстового контента. Нарушение этого порядка вернёт ошибку 400.
После получения tool_result модель продолжает генерацию. Если нужен ещё один инструмент — stop_reason снова будет "tool_use". Цикл повторяется, пока не придёт "end_turn".
sequenceDiagram
participant App as Ваше приложение
participant API as Claude API
App->>API: messages + tools
API-->>App: stop_reason tool_use
Note over App: Выполнить name(input)
App->>API: messages + tool_result
API-->>App: stop_reason end_turn
Note over App: Или снова tool_use — циклКонтроль вызовов: tool_choice и strict
По умолчанию tool_choice: {"type": "auto"} — модель сама решает, вызывать инструмент или отвечать текстом. Три дополнительных варианта:
tool_choice | Поведение |
|---|---|
{"type": "any"} | Обязательно вызвать хотя бы один инструмент |
{"type": "tool", "name": "..."} | Только этот конкретный инструмент |
{"type": "none"} | Инструменты заблокированы в этом ответе |
При any и tool модель не пишет текст перед вызовом — сразу переходит к tool_use. Добавьте "strict": true в определение инструмента, чтобы гарантировать точное соответствие аргументов схеме без лишних полей и без пропуска required-параметров.
Помимо клиентских инструментов есть серверные — их выполняет инфраструктура Anthropic, tool_result слать не нужно. Подключаются через тот же tools, но с именованным типом:
tools=[{"type": "web_search_20260209", "name": "web_search"}]MCP-коннектор: внешние серверы из API
В Claude Code MCP-серверы добавляются командой claude mcp add. В Messages API — параметр mcp_servers (бета-функция, заголовок mcp-client-2025-11-20):
response = client.beta.messages.create(
model="claude-opus-4-8",
max_tokens=1000,
messages=[{"role": "user", "content": "Покажи открытые PR"}],
mcp_servers=[
{
"type": "url",
"url": "https://github-mcp.example.com/sse",
"name": "github",
"authorization_token": "ghp_..."
}
],
tools=[{"type": "mcp_toolset", "mcp_server_name": "github"}],
betas=["mcp-client-2025-11-20"],
)Сервер должен быть публично доступен по HTTPS (SSE или Streamable HTTP) — stdio-серверы через этот механизм не работают. Параметр mcp_servers описывает соединение; tools с типом mcp_toolset — какие инструменты включить (по умолчанию все).

Для блокировки опасных инструментов (denylist):
{
"type": "mcp_toolset",
"mcp_server_name": "github",
"configs": {"delete_repository": {"enabled": false}}
}Для allowlist — задайте "default_config": {"enabled": false}, затем включите нужные через configs поимённо. Ответ содержит блоки mcp_tool_use и mcp_tool_result — аналогичны обычным, плюс поле server_name.
Стриминг
По умолчанию API ждёт полного ответа и отдаёт его разом. Для длинных генераций — несколько секунд молчания перед первым символом. stream: true переключает на Server-Sent Events.
В Python SDK — метод .stream():
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=2048,
messages=[{"role": "user", "content": "Объясни GC в CPython"}],
) as stream:
for chunk in stream.text_stream:
print(chunk, end="", flush=True)
final = stream.get_final_message()
print(f"\nТокенов: {final.usage.input_tokens} in / {final.usage.output_tokens} out")В TypeScript:
await client.messages
.stream({
model: "claude-sonnet-4-6",
max_tokens: 2048,
messages: [{ role: "user", content: "Объясни GC в CPython" }],
})
.on("text", (t) => process.stdout.write(t));Стриминг с инструментами. Когда модель стримит вызов инструмента, вместо text_delta приходят события input_json_delta с частичным JSON аргументов:
event: content_block_delta
data: {"type":"content_block_delta","index":1,
"delta":{"type":"input_json_delta","partial_json":"{\"path\": \"/src/"}}Склеивайте строки partial_json до события content_block_stop, потом парсите JSON. SDK делает это автоматически — в text_stream данные инструментов не попадают. Следите за финальным stop_reason в событии message_delta: если "tool_use" — нужна следующая итерация цикла.
See also
- Claude API и Anthropic SDK: основы — Messages API, семейство моделей, stop_reason
- Claude Agent SDK: программная сборка агентов — готовый агентный цикл без ручного управления tool loop
- Промпт-кэширование, батчи и оптимизация затрат — кэширование инструментальных схем снижает стоимость
- Model Context Protocol: архитектура и основы — что такое MCP как открытый стандарт
- Подключение MCP-серверов в Claude Code — то же, но через интерфейс Claude Code
- Субагенты и контекстная изоляция — альтернативный паттерн оркестрации