AI-разработка

Как Claude Code, Codex и другие AI Coding Agent вызывают MCP Tools: JSON-RPC, Tool Schema и права (2026)

Около 16 мин

Текст про протокол уже развёл Schema и Function Calling. Этот садится только на coding agent: модель заполняет аргументы; хозяин шлёт JSON-RPC. Подключить, перечислить, Schema, потом права — четыре шага, не фраза «подключи MCP».

Перед чтением

Шаг Что запирает Кто исполняет
Конфиг .mcp.json / config.toml stdio или http; url нужен type
Открытие tools/list + inputSchema Хозяин перечисляет; модель провод не видит
Вызов arguments в tools/call Модель заполняет JSON; хозяин шлёт RPC
Права mcp__server__tool / enabled_tools Подключено ≠ автозапуск

MCP — протокол хозяина и сервера. Модель видит имена инструментов и Schema параметров.

MCP зовёт хозяин, не модель

Claude Code, Codex и Cursor — клиенты MCP. После старта сессии хозяин подключается, шлёт JSON-RPC 2.0 tools/list и отдаёт модели каждый inputSchema. Модель выдаёт только JSON аргументов. Хозяин шлёт tools/call и даёт побочные эффекты. Без этого прыжка MCP — файл конфига.

  • Конфиг: как стартует сервер. stdio — локальный процесс. http — удалённо. Оба — объекты, не устный договор.
  • Schema: order_id должен пройти inputSchema. Широкий object приглашает выдуманные ключи.
  • Права: Claude пишет mcp__сервер__инструмент. Codex держит enabled_tools и режим одобрения. Cursor — mcpAllowlist. Подключить сервер эти двери не открывает.

Schema против Function Calling на слое протокола: MCP Tool Schema vs Function Calling. Свой JSON инструментов coding agent: AI coding agent и JSON-вызовы инструментов. Почему Schema — дверь: Зачем ИИ нужен JSON Schema. Этот текст слова не переопределяет. Он только показывает, как хозяин гоняет MCP.

Первый объект: конфиг сервера

Проектный файл Claude Code — .mcp.json в корне репозитория. Его можно класть в систему версий. Личные записи живут в ~/.claude.json. У url должен быть type: http (streamable-http — псевдоним) или sse / ws. url без type считают сломанным stdio и пропускают. Вымышленный сервис заказов — без настоящих секретов:

{
  "mcpServers": {
    "orders": {
      "type": "stdio",
      "command": "node",
      "args": ["./servers/orders-mcp.js"]
    }
  }
}

Codex пишет тот же факт в ~/.codex/config.toml или в .codex/config.toml доверенного проекта, таблица [mcp_servers.orders]. stdio — command / args. Удалённое — url. Cursor — .cursor/mcp.json или пользовательский файл; редактор и CLI делят его. Новый сервер проекта в Claude Code ждёт одобрения. Сам не подключается.

Второй объект: tools/list и inputSchema

После подключения хозяин спрашивает, какие инструменты есть. Это JSON-RPC, не HTTP от модели:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list"
}

Сервер возвращает таблицу. Каждая строка — имя, описание и JSON Schema. Спека требует способность tools. Список может меняться с авторизацией. Не должен дрожать на каждое соединение. Coding agent превращает таблицу в инструменты для модели. В Claude Code вызываемое имя — mcp__orders__lookup_order. Сервер из плагина ещё вкладывает имя плагина. Если таблица большая, официальные клиенты откладывают инструменты и раскрывают поиском. Не сыпать всю таблицу в контекст по умолчанию.

{
  "name": "lookup_order",
  "description": "Look up an order by ID. Confirm before any refund.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "order_id": { "type": "string", "pattern": "^[A-Z]-\\d{4}$" }
    },
    "required": ["order_id"],
    "additionalProperties": false
  }
}

Третий объект: модель заполняет, хозяин tools/call

Модель видит имя, которое опубликовал хозяин. В сессии Claude Code вызов выглядит так:

{
  "type": "tool_use",
  "id": "call_3c91",
  "name": "mcp__orders__lookup_order",
  "input": { "order_id": "A-1042" }
}

Хозяин гоняет input через inputSchema и переводит в RPC серверу. Имя на проводе — зарегистрированное lookup_order, без префикса mcp__:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "lookup_order",
    "arguments": { "order_id": "A-1042" }
  }
}

При успехе вернуть content. При провале поставить isError и записать причину объектом. Не бросать одну фразу прозы. Модель читает запись назад, не сокет.

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      { "type": "text", "text": "{\"order_id\":\"A-1042\",\"status\":\"paid\"}" }
    ],
    "isError": false
  }
}

Четвёртый объект: права исполнения

Спека MCP говорит: вызов инструмента должен быть отклоняемым. Coding agent пишет отказ правилами, не prompt. Claude Code использует полное вызываемое имя:

{
  "permissions": {
    "allow": ["mcp__orders__lookup_order"],
    "ask": ["mcp__orders__create_note"],
    "deny": ["mcp__orders__refund_order"]
  }
}

Официальный порядок всё ещё deny, ask, allow. Голый deny mcp__* вынимает MCP из контекста. Codex держит enabled_tools как белый список, disabled_tools как поздний чёрный и auto / prompt / approve по умолчанию, с перекрытием на инструмент. Cursor по умолчанию спрашивает; mcpAllowlist в permissions.json пишется как orders:lookup_order. Двери Shell / Git / файлы: Модель прав coding agent. MCP — четвёртая таблица, не замена тех трёх.

Пять шагов к исполняемому MCP

Не начинать с «пожалуйста, подключи MCP». Писать объекты:

  1. Сначала конфиг сервера. Выбрать stdio или http. type на каждый url. Секреты — в переменных среды, не в репозитории.
  2. Самим прогнать tools/list. Записать имена и required. Инструмент без Schema модели не отдавать.
  3. Затянуть inputSchema. pattern, enum, additionalProperties: false. Поиск не должен обрастать ключом возврата.
  4. Права писать на инструмент. Только чтение — allow. Запись — ask. Возврат — deny. Codex: сначала резать таблицу enabled_tools.
  5. Три JSON-следа одного успеха: tool_use, tools/call, result. Если сходятся — прыжок настоящий.

Как Schema становится дверью: Agents API + JSON Schema. Этот текст требует лишь, чтобы каждый шаг MCP на coding agent был объектом, который шлёт хозяин.

Разбирать сбой MCP — разложить три JSON-следа

Провал обычно — имя, Schema или права, не модель, которая «не умеет MCP».

  • формат inputSchema рядом с arguments. Искать дыры и лишние ключи.
  • извлечь JSONPath вытащить $.params.name и $.input.order_id.
  • JSON Diff проход и отказ. Сначала правило и Schema, потом prompt.
  • Сверить имя хозяина mcp__orders__lookup_order с именем RPC lookup_order. У серверов плагина есть ещё сегмент плагина.
  • Не вставлять конфиг, токены и логи сессии в публичный форматтер. JSONSort работает в браузере локально.

Если три следа собираются по Schema, вы собрали MCP. Иначе собрали «я нажал подключить».

Частые ловушки

  • Считать подключение вызовом. Подключение значит лишь: процесс или HTTP живы. Без list, Schema и прав модель всё ещё не зовёт.
  • Считать MCP Function Calling. Для модели оба — заполнить JSON. Для хозяина MCP добавляет прыжок JSON-RPC. Текст протокола уже развёл слова. Здесь нужно только увидеть прыжок.
  • url без type. Claude Code пропускает его как сломанный stdio. Удалённый сервер пропадает.
  • Настроить сервер и не резать инструменты. Большая таблица входит в контекст. Официальная отсрочка и enabled_tools существуют из-за этого.
  • Ставить «автоодобрить все MCP» по умолчанию. Полное одобрение Cursor, bypass Claude и auto везде у Codex — для изолированных сред. Будни пишут по инструменту.
  • Считать проектный .mcp.json личным файлом. Он общий в системе версий. Коллеги увидят ожидание одобрения. Секреты остаются в пользовательском конфиге.

FAQ

Шлёт ли coding agent JSON-RPC сам?

Нет. Модель заполняет имя инструмента и arguments. Хозяин собирает id, method и params. Если в логе tools/call — это клиент, не модель.

Какая дверь первая: inputSchema или права?

Сначала форма, потом исполнение. Schema не прошла — RPC не слать. Прошла — ask или deny всё ещё могут сработать. Двери запирают разное.

stdio или http?

Локальные файлы или процесс: stdio. Размещённый сервис: http. Claude Code для удалённого предпочитает http; SSE устарел. WebSocket — если сервер сам толкает события.

Слишком много инструментов?

Claude Code может отложить инструменты MCP и раскрыть поиском. Codex режет таблицу enabled_tools. Не сыпать пятьдесят инструментов в одну сессию.

Может ли субагент пользоваться этими MCP?

Зависит от продукта. Права и allowed-tools Claude Code хотят полное вызываемое имя. Субагенты Agents API наследуют MCP, не ваши function. Не считать, что одна таблица копируется в каждую дочернюю сессию.

Вывод

Coding agent, который зовёт MCP, — не модель, бьющая в сервер. Хозяин перечисляет, модель заполняет JSON, хозяин затем tools/call. Schema и права — две двери.

Запишите, какое имя инструмента, какая Schema и какое правило возьмёт A-1042. Если можете указать на три объекта, MCP подключён. Модели сменятся. Эти три должны устояться раньше.

Дальше

Журнал изменений: Первая публикация