routerai-image-mcp: MCP-сервер генерации картинок через RouterAI

- server.py: MCP-сервер (stdio + streamable HTTP), инструменты generate_image / list_image_models
- два бэкенда: chat/completions (Gemini Image, понимает правку по референсу) и images/generations (FLUX, GPT-Image)
- ключ RouterAI берётся из env или ~/.hermes/models.yaml автоматически
- retry на 429/5xx, картинка сохраняется файлом (~/.hermes/image_cache/mcp-gen)
- test_client.py / test_edit.py: проверка по протоколу, включая image-to-image
- README: установка, настройка, подключение к Hermes/opencode, питфоллы
This commit is contained in:
2026-09-24 11:01:23 +03:00
commit 585c6a4ca4
7 changed files with 793 additions and 0 deletions
+222
View File
@@ -0,0 +1,222 @@
# routerai-image-mcp
MCP-сервер генерации и редактирования картинок через **RouterAI**
(`https://routerai.ru/api/v1`). Даёт любой модели — в том числе той, что
рисовать не умеет, но умеет ставить ТЗ другим — инструмент `generate_image`:
передал текстовое описание, получил файл с картинкой.
## Зачем
Агентные модели (DeepSeek, Qwen, локальные) отлично формулируют ТЗ, но не
генерируют изображения. Этот сервер закрывает разрыв: модель вызывает
`generate_image(prompt=...)`, сервер ходит в модель генерации картинок
(Nano Banana / Gemini Image, FLUX, GPT-Image), кладёт результат файлом и
возвращает путь. Модель вставляет этот путь в ответ — клиент показывает картинку.
## Архитектура
```
LLM (agent) ──MCP──▶ routerai-image-mcp ──HTTPS──▶ routerai.ru/api/v1
│
└─▶ файл на диске (~/.hermes/image_cache/mcp-gen/)
возвращает: "MEDIA:<путь>"
```
Два бэкенда, выбираются автоматически по id модели:
| Backend | Что вызывается | Модели |
|---|---|---|
| `chat` | `POST /chat/completions` с `modalities: ["image","text"]` | Gemini Image / Nano Banana |
| `images` | `POST /images/generations` | FLUX.2, GPT-Image |
`chat`-бэкенд дополнительно умеет **image-to-image**: если передать
`reference_images`, они уходят в запрос как image_url-части, и модель
редактирует присланную картинку (сохранив сюжет) — проверено на «поменяй фон».
## Требования
- Python 3.10+
- Пакет `mcp` (`pip install mcp`) — в Hermes он уже есть:
`/usr/local/lib/hermes-agent/venv/bin/python`
- Ключ RouterAI (см. «Настройка»)
- Сетевой доступ к `https://routerai.ru`
## Установка
```bash
git clone https://git.binom.pw/subochev/routerai-image-mcp
cd routerai-image-mcp
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
```
Либо без своего venv — использовать интерпретатор Hermes (там `mcp` уже стоит):
```bash
/usr/local/lib/hermes-agent/venv/bin/python server.py
```
## Настройка
Ключ ищется в таком порядке:
1. `ROUTERAI_API_KEY` (или `IMAGE_MCP_API_KEY`) в окружении;
2. `~/.hermes/models.yaml` → `models.deepseek.api_key` (если у записи
`base_url` содержит `routerai.ru`);
3. любой подходящий `api_key` из `models.yaml`, где `base_url` = routerai.ru.
То есть на машине с Hermes, где `models.yaml` уже настроен, **ничего задавать
не нужно** — ключ подхватится сам. Явный способ:
```bash
export ROUTERAI_API_KEY='sk-...'
```
Переменные окружения сервера:
| Переменная | Дефолт | Смысл |
|---|---|---|
| `ROUTERAI_API_KEY` | — | Ключ RouterAI (иначе берётся из `models.yaml`) |
| `ROUTERAI_BASE_URL` | `https://routerai.ru/api/v1` | Базовый URL API |
| `IMAGE_MCP_DEFAULT_MODEL` | `google/gemini-3.1-flash-image` | Модель по умолчанию |
| `IMAGE_MCP_OUT_DIR` | `~/.hermes/image_cache/mcp-gen` | Куда складывать картинки |
| `IMAGE_MCP_TIMEOUT` | `300` | Таймаут запроса, сек |
| `MCP_TRANSPORT` | `stdio` | `stdio` или `http` |
| `MCP_HOST` / `MCP_PORT` | `0.0.0.0` / `8790` | Только для `http` |
## Запуск
### stdio (обычный режим для локальных клиентов)
```bash
python server.py
```
### HTTP (когда сервер нужен по сети нескольким клиентам)
```bash
MCP_TRANSPORT=http MCP_PORT=8790 python server.py
```
Эндпоинт: `http://<host>:8790/mcp` (streamable HTTP). Проверка:
```bash
curl -s -X POST http://127.0.0.1:8790/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"probe","version":"1"}}}'
# → event: message / data: {... "serverInfo":{"name":"routerai-image"}}
```
## Подключение к Hermes
```bash
hermes mcp add routerai-image \
--command /usr/local/lib/hermes-agent/venv/bin/python \
--args /opt/routerai-image-mcp/server.py
```
На вопрос «Enable all N tools?» ответить `Y`. Проверка:
```bash
hermes mcp test routerai-image
# ✓ Connected, Tools discovered: 2 → generate_image, list_image_models
```
⚠️ **Инструменты появляются только в новой сессии** — MCP-серверы
подключаются при старте Hermes; в уже идущем диалоге их не будет.
Если сервер развёрнут на отдельной машине, вместо stdio удобнее HTTP:
```bash
hermes mcp add routerai-image --url http://<host>:8790/mcp
```
## Подключение к другим клиентам
opencode (`~/.config/opencode/config.json`):
```json
{
"mcp": {
"routerai-image": {
"type": "local",
"command": ["/usr/local/lib/hermes-agent/venv/bin/python", "/opt/routerai-image-mcp/server.py"],
"enabled": true
}
}
}
```
Claude Desktop / прочие stdio-клиенты — тот же `command` + `args`.
## Инструменты
### `generate_image`
| Аргумент | Тип | Смысл |
|---|---|---|
| `prompt` | str | ТЗ: сюжет, стиль, свет, композиция, пропорции |
| `model` | str | id модели RouterAI (дефолт `google/gemini-3.1-flash-image`) |
| `reference_images` | list[str] | Пути к картинкам для правки/референса (image-to-image) |
| `out_dir` | str | Куда сложить файл |
| `filename` | str | Имя файла (расширение подставится само) |
Возвращает текст с абсолютным путём и строкой `MEDIA:<путь>`. **Чтобы клиент
показал картинку, модель должна вставить эту строку `MEDIA:<путь>` целиком в
свой ответ.**
### `list_image_models`
Печатает список поддерживаемых моделей, их бэкенды и дефолт.
## Модели и стоимость
| id | Что это | Цена (факт, за картинку) |
|---|---|---|
| `google/gemini-3.1-flash-image` | Nano Banana 2 — дефолт | ~7.5 ₽ |
| `google/gemini-3.1-flash-lite-image` | Nano Banana 2 Lite — самый дешёвый | ~3.7 ₽ |
| `google/gemini-2.5-flash-image` | Nano Banana (2.5) | ~4.3 ₽ |
| `google/gemini-3-pro-image` | Nano Banana Pro | ~13 ₽ |
| `black-forest-labs/flux.2-klein-4b` | FLUX.2 Klein — дешёвый, без правки | — |
| `black-forest-labs/flux.2-pro` / `-max` / `-flex` | FLUX.2 Pro/Max/Flex | — |
| `openai/gpt-image-1` / `-mini` | GPT Image | — |
Ключи без значения — считаются по своему тарифу, проверяйте на
`GET https://routerai.ru/api/v1/models` (поле `pricing.image_output`).
Gemini-Image дороже других, зато понимает сложные ТЗ и умеет правку по референсу.
## Проверка
```bash
# список инструментов по протоколу + генерация
/usr/local/lib/hermes-agent/venv/bin/python test_client.py --generate google/gemini-3.1-flash-lite-image
# правка существующей картинки по референсу
/usr/local/lib/hermes-agent/venv/bin/python test_edit.py /путь/к/картинке.jpg
```
Оба скрипта поднимают сервер по stdio и работают с ним как настоящий MCP-клиент.
## Питфоллы
- **`llm.binom.pw` может лежать (502), а `routerai.ru` — работать.** Сервер
ходит напрямую в `routerai.ru` и не зависит от Bifrost/llm-proxy. Если у вас
всё настроено через роутер и он упал — картинки всё равно будут.
- **Путь к интерпретатору.** `python3` из системы может не иметь пакета `mcp`.
В Hermes надёжнее указывать его venv: `/usr/local/lib/hermes-agent/venv/bin/python`.
- **Картинки нужны модели, а не агенту.** Возвращается путь к файлу, а не
base64 в контекст — контекст не забивается мегабайтами.
- **Модели, отдающие картинки иначе.** `flux.2-*` и `gpt-image-*` не принимают
`modalities` — для них используется `/images/generations`; определяется
автоматически по таблице `MODELS` в начале `server.py`. Новая модель из
другого семейства — дописать её туда.
- **RouterAI отдаёт префикс-мусор перед JSON** на `/images/generations`
(переводы строк и пробелы) — парсер ищет первый `{`.
- **429/5xx** — сервер сам делает 3 повтора с нарастающей паузой; 403
`model_blocked` означает, что модель не разрешена ключу на роутере.
## Лицензия
Apache License 2.0 — см. `LICENSE`.