agent-api-server — Claude по подписке как OpenAI API
~/.claude/tools/agent_api_server.py — stdlib-only HTTP-сервер, который принимает
запросы в формате OpenAI и обслуживает их локальным claude -p. Оплата идёт через
подписку Claude Code (OAuth), ANTHROPIC_API_KEY не используется и намеренно
вырезается из окружения дочернего процесса.
Направление трафика — не путать
| Инструмент |
Куда |
Что делает |
| Свой прокси-гейтвей к внешним провайдерам |
НАРУЖУ |
проксирует OpenAI/Perplexity/Runway; в пак не входит — если нужен, поднимается отдельно (например, LiteLLM) |
agent-api-server (127.0.0.1:8199) |
ВНУТРЬ |
отдаёт Claude-по-подписке как OpenAI-совместимый эндпоинт |
claude-cli-runner / claude_cli.py |
— |
разовый вызов из Python-кода, без HTTP |
Когда использовать
- n8n-нода «OpenAI Chat Model» должна ходить в Claude без покупки API-ключа.
- Скрипт/IDE/чат-фронт (Open WebUI, LibreChat, Cursor-подобные) умеет только OpenAI-протокол.
- Нужен SSE-стрим ответа Claude в стороннее приложение.
- Нужен диалог с состоянием на стороне сервера (заголовок
X-Session-Id).
Не использовать: когда достаточно python claude_cli.py "prompt"; когда нужен агент с
тулзами/памятью/кроном (это Hermes → autonomous-agent-creator).
Установка / настройка
# 1. Зависимости: только Python 3.9+ (stdlib) и claude CLI
claude --version # 2.1.207 проверено
npm install -g @anthropic-ai/claude-code # если CLI нет
# 2. Проверка
python ~/.claude/tools/agent_api_server.py test --model haiku
# 3. Запуск
python ~/.claude/tools/agent_api_server.py serve --port 8199
Env-переменные (значения заполняет владелец; читаются из ~/.claude/.credentials.master.env)
| Переменная |
Обязательна |
Смысл |
AGENT_API_TOKEN |
нет (ДА при host ≠ 127.0.0.1) |
Bearer-токен для /v1/*; без него сервер на loopback пускает всех |
AGENT_API_MODEL |
нет |
модель по умолчанию (иначе sonnet) |
AGENT_API_HOST |
нет |
хост по умолчанию (иначе 127.0.0.1) |
AGENT_API_PORT |
нет |
порт по умолчанию (иначе 8199) |
AGENT_API_TIMEOUT |
нет |
таймаут одного вызова CLI, сек (иначе 600) |
AGENT_API_WORKDIR |
нет |
cwd для CLI (иначе ~/.claude/agent-api-workdir) |
AGENT_API_MAX_CONCURRENCY |
нет |
сколько CLI-процессов одновременно (иначе 4) |
CLAUDE_CLI_PATH |
нет |
путь к claude, если не находится в PATH |
Команды
| Команда |
Что делает |
serve |
поднять сервер (блокирующе) |
serve --port N --host H |
порт/хост (не-loopback требует AGENT_API_TOKEN, иначе exit 2) |
serve --model M |
модель по умолчанию: opus/fable/sonnet/haiku или полный id |
serve --allow-tools |
разрешить агенту тулзы Claude Code (Bash/Read/Edit). По умолчанию ВЫКЛ — чистая генерация текста |
serve --timeout 900 |
бюджет на один запрос, сек |
serve --inherit-anthropic-env |
НЕ вырезать ANTHROPIC_* из env дочернего процесса (по умолчанию вырезаются) |
serve --quiet / --json |
без access-лога / JSON-баннер старта |
test |
самопроверка: поднять на свободном порту → /health, /v1/models, 400 на пустых messages, короткий чат, SSE-стрим → погасить |
test --skip-chat |
только HTTP-обвязка, без траты модельного вызова |
test --json |
машинный вывод {ok, checks[]} |
models / models --json |
что отдаёт /v1/models + найден ли CLI |
HTTP API
| Метод |
Путь |
Примечание |
| POST |
/v1/chat/completions |
messages[], model, stream (true → SSE data: ... + [DONE]) |
| GET |
/v1/models |
список моделей |
| GET |
/health |
без авторизации: cli_found, cli_path, cli_version, workdir, uptime_s |
| GET |
/ |
короткая справка текстом |
Заголовки: X-Session-Id: <строка ≤128> — продолжить диалог; в ответе эхо + X-Claude-Session-Id
(реальный UUID сессии CLI). Authorization: Bearer <AGENT_API_TOKEN> — если токен задан.
Примеры
curl (нестримом)
curl -X POST http://127.0.0.1:8199/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"haiku","messages":[{"role":"user","content":"Reply with exactly: PONG"}]}'
curl (стрим + сессия)
curl -N -X POST http://127.0.0.1:8199/v1/chat/completions \
-H "Content-Type: application/json" -H "X-Session-Id: n8n-lead-42" \
-d '{"model":"sonnet","stream":true,"messages":[{"role":"user","content":"Напиши хайку про кэш"}]}'
OpenAI SDK (Python) — проверено живьём
from openai import OpenAI
c = OpenAI(base_url="http://127.0.0.1:8199/v1", api_key="not-needed") # или AGENT_API_TOKEN
print(c.chat.completions.create(model="haiku",
messages=[{"role":"user","content":"Reply with exactly: SDKOK"}]).choices[0].message.content)
for chunk in c.chat.completions.create(model="haiku", stream=True,
messages=[{"role":"user","content":"Count: one two three"}]):
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")
n8n
- Нода: OpenAI Chat Model (или HTTP Request на
/v1/chat/completions).
- Credential «OpenAI»:
Base URL = http://127.0.0.1:8199/v1, API Key = значение AGENT_API_TOKEN
(если токен не задан — любая непустая строка, поле в n8n обязательное).
- Model: вписать вручную
sonnet / haiku / fable / opus.
- ⚠️ n8n в Docker:
127.0.0.1 внутри контейнера — это сам контейнер. Нужен
http://host.docker.internal:8199/v1, а сервер поднимать с --host 0.0.0.0 + AGENT_API_TOKEN
(требует проверки на конкретной инсталляции n8n владельца — не тестировалось).
- n8n Cloud (your-name.app.n8n.cloud) до
127.0.0.1 не дотянется в принципе — нужен туннель.
Гочи (все найдены/проверены живьём 2026-07-25)
ANTHROPIC_API_KEY + ANTHROPIC_CUSTOM_HEADERS из .credentials.master.env ломают CLI.
Симптом: API Error: Invalid header name: '{"anthropic-beta"', HTTP 502. Причина: load_env()
тянет весь файл в окружение, а дочерний claude его наследует. Сервер вырезает ANTHROPIC_*
(+ CLAUDE_CODE_USE_BEDROCK/VERTEX, CLAUDE_CODE_SESSION_ID, CLAUDE_CODE_CHILD_SESSION).
Побочный эффект того же фикса: биллинг гарантированно идёт по подписке, а не по API-ключу.
Ломать защиту только осознанно: --inherit-anthropic-env.
- Флаги
--system и --max-tokens в claude 2.1.207 не существуют. Правильные —
--system-prompt (замена) / --append-system-prompt (добавка); лимита токенов флагом нет
вообще, длину задавай в промпте. Обёртка ~/.claude/tools/claude_cli.py этой ошибкой болела
и была починена; сервер всё равно берёт оттуда только обнаружение CLI, а не сборку argv.
- Windows:
claude резолвится в claude.CMD. CreateProcess не запускает .cmd напрямую —
сервер сам оборачивает в cmd.exe /c.
- Стрим требует трёх флагов сразу:
--output-format stream-json --include-partial-messages --verbose.
Без --verbose CLI не отдаёт stream_event.
thinking_delta в стрим не отдаётся — наружу идёт только text_delta, иначе клиент увидит
рассуждения как ответ.
usage.prompt_tokens большой (десятки тысяч) даже на «привет». Это не баг: каждый запуск CLI
прогревает системный промпт + CLAUDE.md (cache_creation). Считать по нему деньги нельзя.
- Холодный старт ~8-12 с на запрос (спавн Node-процесса + прогрев контекста). Для чат-UI это
заметно; для n8n/бэкграунда нормально. Уменьшить:
--model haiku.
- CLAUDE.md и хуки владельца всё равно подгружаются (user-level). Отвязать полностью можно было
бы через
--bare, но он требует API-ключ и убивает смысл подписки. AGENT_API_WORKDIR управляет
только проектным контекстом.
- Тулзы по умолчанию отключены (
--tools ""): в неинтерактивном режиме запрос разрешения
некому подтвердить. --allow-tools включает их — это значит, что HTTP-клиент получает право
выполнять Bash на машине владельца. Включать только с AGENT_API_TOKEN.
- Сессии живут в CLI, не в сервере.
X-Session-Id → UUIDv5 → --session-id (1-й ход) /
--resume (далее); реестр ~/.claude/agent_api_sessions.json. Если удалить реестр — следующий
ход снова пойдёт как первый и потеряет историю (сама сессия CLI при этом останется на диске).
- В stateful-режиме на бэкенд уходит только последнее user-сообщение — историю держит CLI.
В stateless-режиме весь
messages[] схлопывается в один промпт с префиксом «Conversation so far».
- Картинки/файлы не поддерживаются — multimodal-части заменяются маркером
[image input not supported by claude-cli backend].
- Параллелизм ограничен 4 (
AGENT_API_MAX_CONCURRENCY); при переполнении — HTTP 429 через 10 с
ожидания. Каждый запрос — отдельный Node-процесс, не поднимать лимит бездумно.
- Не-loopback хост без токена = отказ стартовать (exit 2). Это осознанно.
Чек-лист
1---2name: agent-api-server3description: Claude по подписке как OpenAI API (/v1/chat/completions поверх claude CLI) для n8n/IDE.4---56# agent-api-server — Claude по подписке как OpenAI API78`~/.claude/tools/agent_api_server.py` — stdlib-only HTTP-сервер, который принимает9запросы в формате OpenAI и обслуживает их локальным `claude -p`. Оплата идёт через10подписку Claude Code (OAuth), `ANTHROPIC_API_KEY` не используется и **намеренно11вырезается** из окружения дочернего процесса.1213## Направление трафика — не путать1415| Инструмент | Куда | Что делает |16|---|---|---|17| Свой прокси-гейтвей к внешним провайдерам | НАРУЖУ | проксирует OpenAI/Perplexity/Runway; в пак не входит — если нужен, поднимается отдельно (например, LiteLLM) |18| **`agent-api-server`** (127.0.0.1:**8199**) | ВНУТРЬ | отдаёт Claude-по-подписке как OpenAI-совместимый эндпоинт |19| `claude-cli-runner` / `claude_cli.py` | — | разовый вызов из Python-кода, без HTTP |2021## Когда использовать2223- n8n-нода «OpenAI Chat Model» должна ходить в Claude без покупки API-ключа.24- Скрипт/IDE/чат-фронт (Open WebUI, LibreChat, Cursor-подобные) умеет только OpenAI-протокол.25- Нужен SSE-стрим ответа Claude в стороннее приложение.26- Нужен диалог с состоянием на стороне сервера (заголовок `X-Session-Id`).2728Не использовать: когда достаточно `python claude_cli.py "prompt"`; когда нужен агент с29тулзами/памятью/кроном (это Hermes → `autonomous-agent-creator`).3031## Установка / настройка3233```bash34# 1. Зависимости: только Python 3.9+ (stdlib) и claude CLI35claude --version # 2.1.207 проверено36npm install -g @anthropic-ai/claude-code # если CLI нет3738# 2. Проверка39python ~/.claude/tools/agent_api_server.py test --model haiku4041# 3. Запуск42python ~/.claude/tools/agent_api_server.py serve --port 819943```4445### Env-переменные (значения заполняет владелец; читаются из `~/.claude/.credentials.master.env`)4647| Переменная | Обязательна | Смысл |48|---|---|---|49| `AGENT_API_TOKEN` | нет (ДА при host ≠ 127.0.0.1) | Bearer-токен для `/v1/*`; без него сервер на loopback пускает всех |50| `AGENT_API_MODEL` | нет | модель по умолчанию (иначе `sonnet`) |51| `AGENT_API_HOST` | нет | хост по умолчанию (иначе `127.0.0.1`) |52| `AGENT_API_PORT` | нет | порт по умолчанию (иначе `8199`) |53| `AGENT_API_TIMEOUT` | нет | таймаут одного вызова CLI, сек (иначе `600`) |54| `AGENT_API_WORKDIR` | нет | cwd для CLI (иначе `~/.claude/agent-api-workdir`) |55| `AGENT_API_MAX_CONCURRENCY` | нет | сколько CLI-процессов одновременно (иначе `4`) |56| `CLAUDE_CLI_PATH` | нет | путь к `claude`, если не находится в PATH |5758## Команды5960| Команда | Что делает |61|---|---|62| `serve` | поднять сервер (блокирующе) |63| `serve --port N --host H` | порт/хост (не-loopback требует `AGENT_API_TOKEN`, иначе exit 2) |64| `serve --model M` | модель по умолчанию: `opus`/`fable`/`sonnet`/`haiku` или полный id |65| `serve --allow-tools` | разрешить агенту тулзы Claude Code (Bash/Read/Edit). По умолчанию ВЫКЛ — чистая генерация текста |66| `serve --timeout 900` | бюджет на один запрос, сек |67| `serve --inherit-anthropic-env` | НЕ вырезать `ANTHROPIC_*` из env дочернего процесса (по умолчанию вырезаются) |68| `serve --quiet` / `--json` | без access-лога / JSON-баннер старта |69| `test` | самопроверка: поднять на свободном порту → `/health`, `/v1/models`, 400 на пустых messages, короткий чат, SSE-стрим → погасить |70| `test --skip-chat` | только HTTP-обвязка, без траты модельного вызова |71| `test --json` | машинный вывод `{ok, checks[]}` |72| `models` / `models --json` | что отдаёт `/v1/models` + найден ли CLI |7374### HTTP API7576| Метод | Путь | Примечание |77|---|---|---|78| POST | `/v1/chat/completions` | `messages[]`, `model`, `stream` (true → SSE `data: ...` + `[DONE]`) |79| GET | `/v1/models` | список моделей |80| GET | `/health` | без авторизации: `cli_found`, `cli_path`, `cli_version`, `workdir`, `uptime_s` |81| GET | `/` | короткая справка текстом |8283Заголовки: `X-Session-Id: <строка ≤128>` — продолжить диалог; в ответе эхо + `X-Claude-Session-Id`84(реальный UUID сессии CLI). `Authorization: Bearer <AGENT_API_TOKEN>` — если токен задан.8586## Примеры8788**curl (нестримом)**89```bash90curl -X POST http://127.0.0.1:8199/v1/chat/completions \91 -H "Content-Type: application/json" \92 -d '{"model":"haiku","messages":[{"role":"user","content":"Reply with exactly: PONG"}]}'93```9495**curl (стрим + сессия)**96```bash97curl -N -X POST http://127.0.0.1:8199/v1/chat/completions \98 -H "Content-Type: application/json" -H "X-Session-Id: n8n-lead-42" \99 -d '{"model":"sonnet","stream":true,"messages":[{"role":"user","content":"Напиши хайку про кэш"}]}'100```101102**OpenAI SDK (Python) — проверено живьём**103```python104from openai import OpenAI105c = OpenAI(base_url="http://127.0.0.1:8199/v1", api_key="not-needed") # или AGENT_API_TOKEN106print(c.chat.completions.create(model="haiku",107 messages=[{"role":"user","content":"Reply with exactly: SDKOK"}]).choices[0].message.content)108109for chunk in c.chat.completions.create(model="haiku", stream=True,110 messages=[{"role":"user","content":"Count: one two three"}]):111 if chunk.choices and chunk.choices[0].delta.content:112 print(chunk.choices[0].delta.content, end="")113```114115**n8n**116- Нода: *OpenAI Chat Model* (или *HTTP Request* на `/v1/chat/completions`).117- Credential «OpenAI»: `Base URL` = `http://127.0.0.1:8199/v1`, `API Key` = значение `AGENT_API_TOKEN`118 (если токен не задан — любая непустая строка, поле в n8n обязательное).119- Model: вписать вручную `sonnet` / `haiku` / `fable` / `opus`.120- ⚠️ n8n в Docker: `127.0.0.1` внутри контейнера — это сам контейнер. Нужен121 `http://host.docker.internal:8199/v1`, а сервер поднимать с `--host 0.0.0.0` + `AGENT_API_TOKEN`122 (**требует проверки на конкретной инсталляции n8n владельца — не тестировалось**).123- n8n Cloud (your-name.app.n8n.cloud) до `127.0.0.1` не дотянется в принципе — нужен туннель.124125## Гочи (все найдены/проверены живьём 2026-07-25)1261271. **`ANTHROPIC_API_KEY` + `ANTHROPIC_CUSTOM_HEADERS` из `.credentials.master.env` ломают CLI.**128 Симптом: `API Error: Invalid header name: '{"anthropic-beta"'`, HTTP 502. Причина: `load_env()`129 тянет весь файл в окружение, а дочерний `claude` его наследует. Сервер вырезает `ANTHROPIC_*`130 (+ `CLAUDE_CODE_USE_BEDROCK/VERTEX`, `CLAUDE_CODE_SESSION_ID`, `CLAUDE_CODE_CHILD_SESSION`).131 Побочный эффект того же фикса: биллинг гарантированно идёт по подписке, а не по API-ключу.132 Ломать защиту только осознанно: `--inherit-anthropic-env`.1332. **Флаги `--system` и `--max-tokens` в `claude` 2.1.207 не существуют.** Правильные —134 `--system-prompt` (замена) / `--append-system-prompt` (добавка); лимита токенов флагом нет135 вообще, длину задавай в промпте. Обёртка `~/.claude/tools/claude_cli.py` этой ошибкой болела136 и была починена; сервер всё равно берёт оттуда только обнаружение CLI, а не сборку argv.1373. **Windows: `claude` резолвится в `claude.CMD`.** `CreateProcess` не запускает `.cmd` напрямую —138 сервер сам оборачивает в `cmd.exe /c`.1394. **Стрим требует трёх флагов сразу:** `--output-format stream-json --include-partial-messages --verbose`.140 Без `--verbose` CLI не отдаёт `stream_event`.1415. **`thinking_delta` в стрим не отдаётся** — наружу идёт только `text_delta`, иначе клиент увидит142 рассуждения как ответ.1436. **`usage.prompt_tokens` большой (десятки тысяч) даже на «привет».** Это не баг: каждый запуск CLI144 прогревает системный промпт + CLAUDE.md (cache_creation). Считать по нему деньги нельзя.1457. **Холодный старт ~8-12 с на запрос** (спавн Node-процесса + прогрев контекста). Для чат-UI это146 заметно; для n8n/бэкграунда нормально. Уменьшить: `--model haiku`.1478. **CLAUDE.md и хуки владельца всё равно подгружаются** (user-level). Отвязать полностью можно было148 бы через `--bare`, но он требует API-ключ и убивает смысл подписки. `AGENT_API_WORKDIR` управляет149 только проектным контекстом.1509. **Тулзы по умолчанию отключены** (`--tools ""`): в неинтерактивном режиме запрос разрешения151 некому подтвердить. `--allow-tools` включает их — это значит, что HTTP-клиент получает право152 выполнять Bash на машине владельца. Включать только с `AGENT_API_TOKEN`.15310. **Сессии живут в CLI, не в сервере.** `X-Session-Id` → UUIDv5 → `--session-id` (1-й ход) /154 `--resume` (далее); реестр `~/.claude/agent_api_sessions.json`. Если удалить реестр — следующий155 ход снова пойдёт как первый и потеряет историю (сама сессия CLI при этом останется на диске).15611. **В stateful-режиме на бэкенд уходит только последнее user-сообщение** — историю держит CLI.157 В stateless-режиме весь `messages[]` схлопывается в один промпт с префиксом «Conversation so far».15812. **Картинки/файлы не поддерживаются** — multimodal-части заменяются маркером159 `[image input not supported by claude-cli backend]`.16013. **Параллелизм ограничен 4** (`AGENT_API_MAX_CONCURRENCY`); при переполнении — HTTP 429 через 10 с161 ожидания. Каждый запрос — отдельный Node-процесс, не поднимать лимит бездумно.16214. **Не-loopback хост без токена = отказ стартовать** (exit 2). Это осознанно.163164## Чек-лист165166- [ ] `claude --version` отвечает (иначе `npm install -g @anthropic-ai/claude-code`)167- [ ] `python ~/.claude/tools/agent_api_server.py test --model haiku` → ALL CHECKS PASSED168- [ ] Порт свободен (`serve` печатает понятную ошибку и выходит с кодом 2, если занят)169- [ ] Если наружу/в Docker — задан `AGENT_API_TOKEN` и хост не loopback170- [ ] Клиент шлёт `model` из `/v1/models` (`sonnet`/`haiku`/`fable`/`opus`), а не `gpt-4o`171- [ ] Для диалога с памятью клиент шлёт стабильный `X-Session-Id`172- [ ] `--allow-tools` включён ТОЛЬКО вместе с токеном и осознанно173- [ ] Сервер не оставлен висеть после отладки (`TaskStop` / `Stop-Process`)