Files
subochev 585c6a4ca4 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, питфоллы
2026-09-24 11:01:23 +03:00

223 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`.