Learn (Almost) Anything
КаталогСкачать курс

Claude Code: справочник разработчика

Определение инструментов и цикл tool use, подключение MCP-серверов на уровне API, streaming-ответы. Как дать собственному приложению агентные способности.

ruЭнциклопедия38 уроков8 модулей7
энциклопедия claude codeClaude Codeсправочник разработчикаФундамент Claude Codeпамять и конфигурацияРабочие процессы и best practicesMCPОблачные агентыПрограммный доступAgent SDK и Claude API

Tool use, MCP и потоковая передача в API

Определение инструментов и цикл tool use, подключение MCP-серверов на уровне API, streaming-ответы. Как дать собственному приложению агентные способности.

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.

Проверь себя #cp-1

Инструмент описан одной строкой: "description": "Поиск по файлам". Назовите конкретную проблему, которая из этого вытекает.

Показать ответ

Модель не знает, в каких ситуациях вызывать инструмент, какие значения передавать в параметры, что он возвращает и чего не умеет. Результат — инструмент будет вызываться невпопад или игнорироваться там, где нужен. Описание должно быть из 3–4 предложений: сценарии применения, формат входных данных, что возвращает, явные ограничения.

---

Быстрое повторение #rc-1

Какие три обязательных поля нужны для описания инструмента в Messages API?

Показать ответ

name, description и input_schema. Description — самое критическое: модель решает, вызывать ли инструмент, ориентируясь ровно на него.

Цикл 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 — цикл
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 use: запрос, выполнение инструмента, возврат результата
ИнтерактивПолный цикл tool use на Python#int-1
Шаг 1 из 5Объявляем инструмент
Создаём список `tools` с именем, описанием и схемой входных данных. `description` — самое важное поле.
import anthropic

client = anthropic.Anthropic()

tools = [{
    "name": "get_file_contents",
    "description": (
        "Читает содержимое файла по пути. Использовать для получения "
        "исходного кода или конфигов. Возвращает строку с содержимым."
    ),
    "input_schema": {
        "type": "object",
        "properties": {
            "path": {"type": "string", "description": "Путь к файлу"}
        },
        "required": ["path"]
    }
}]

messages = [{"role": "user", "content": "Покажи содержимое src/main.py"}]
Пошаговая реализация агентного цикла с ручным управлением tool loop.
Проверь себя #cp-2

Вы отправили tool_result и получили новый ответ. stop_reason снова "tool_use". Что происходит и что делать?

Показать ответ

Модель хочет вызвать ещё один инструмент — агентный цикл не завершён. Нужно повторить: выполнить следующий инструмент, добавить tool_result в историю, отправить запрос снова. Цикл завершится, когда stop_reason станет "end_turn". В реальном коде это оборачивают в while response.stop_reason == "tool_use": ....

---

Быстрое повторение #rc-2

Какое значение stop_reason указывает, что модель вызвала инструмент?

Показать ответ

stop_reason: "tool_use". В content содержится блок tool_use с полями id, name и input (аргументы).

Контроль вызовов: 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"}]

---

Быстрое повторение #rc-3

Какое значение tool_choice обязывает модель вызвать хотя бы один инструмент?

Показать ответ

{"type": "any"}. Модель не напишет текст перед вызовом, сразу перейдёт к tool_use.

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 — какие инструменты включить (по умолчанию все).

Официальная архитектурная схема MCP: приложение обращается к Claude API, который по HTTPS подключается к удалённому MCP-серверу и получает его инструменты.

Для блокировки опасных инструментов (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" — нужна следующая итерация цикла.

Проверь себя #cp-3

При стриминге с инструментами приходит событие content_block_delta с "type": "input_json_delta" и "partial_json": "{\"path\": \"/src/". Когда можно распарсить это как полноценный JSON?

Показать ответ

Только после события content_block_stop — до него строка неполная. Нужно накапливать (конкатенировать) все partial_json из последовательных дельт, затем парсить результат одним вызовом json.loads(). Python и TypeScript SDK делают это автоматически; при ручной обработке SSE — следите за content_block_stop.

---

See also

Источники

  1. Tool use with Claude — Overview
  2. Define tools — Anthropic Docs
  3. Handle tool calls — Anthropic Docs
  4. MCP connector — Anthropic Docs
  5. Streaming messages — Anthropic Docs