zai-coding-gateway 0.1.1
MCP-сервер для решения задач в рабочем пространстве через Z.AI Coding Plan (solve_in_workspace, apply_changes)
License: MIT
Requires Python: >=3.11

# ZAI Coding Gateway

MCP-сервер для решения задач в рабочем пространстве через **Z.AI Coding Plan** endpoint (`api.z.ai/api/coding/paas/v4`). Предоставляет инструменты `solve_in_workspace`, `apply_changes`, `confirm_session_done` для любых MCP-клиентов (Cursor, Anigravity, Claude Code, Cline и др.). Один и тот же сервер можно добавить в любую IDE — поведение не зависит от того, откуда вы его запускаете.

## Требования

- Python 3.11+
- Ключ Z.AI и подписка Coding Plan

## Установка

```bash
pip install -e .
# или с тестами:
pip install -e ".[dev]"
```

## Настройка

Все настройки задаются **переменными окружения**. Ключ и модель **не хранятся в коде** — их задаёт пользователь в настройках MCP-клиента (например, в Cursor в `mcp.json` в секции `env`).

| Переменная     | Назначение                                      |
|----------------|--------------------------------------------------|
| `ZAI_API_KEY`  | Ключ Z.AI (обязательный)                        |
| `ZAI_WORK_MODEL` | Необязательно. Модель для генерации кода, рефакторинга и основного цикла сессии. По умолчанию **glm-4.7** (контекст 200K). |
| `ZAI_FAST_MODEL` | Необязательно. Модель для консолидации и суммаризации внутри сессии. По умолчанию **glm-4.5-air** (контекст 128K, быстрая). |
| `ZAI_BASE_URL` | По умолчанию `https://api.z.ai/api/coding/paas/v4` |
| `ZAI_PROJECT_ROOT` | Корень открытого проекта для solve_in_workspace (пути file_paths и сессия). **Чтобы из любой IDE всё работало «как по маслу»**, задайте эту переменную в конфиге MCP так, чтобы она указывала на папку открытого в IDE проекта. Многие IDE при старте MCP подставляют в env переменные вроде `${workspaceFolder}` / `${projectPath}` — тогда один конфиг подходит для Cursor, Anigravity и др. Без неё сервер пробует MCP `roots/list` (многие клиенты пока не поддерживают) или поиск вверх от cwd по маркерам; при неудаче — явная ошибка с просьбой задать ZAI_PROJECT_ROOT. |
| `ZAI_SESSION_LOGS_DIR` | Опционально. Каталог для логов сессий solve_in_workspace. По умолчанию логи пишутся в проект gateway (для разработчиков). Если нужно получать логи в свой проект — укажите абсолютный путь к каталогу (например `${workspaceFolder}/workspace_sessions_logs`). |

## Работа из любой IDE

MCP запускается вашей IDE при открытии проекта. Чтобы пути file_paths/file_path и сессия solve_in_workspace относились к открытому проекту (а не к случайному cwd), в настройках MCP укажите **ZAI_PROJECT_ROOT** в `env`. Тогда не важно, в какой IDE вы работаете (Cursor, Anigravity, другая):

- Если IDE при запуске MCP подставляет в env путь к открытому проекту (например через переменную вроде `${workspaceFolder}`, `${workspaceRoot}`, `%projectPath%` и т.п.) — используйте её для `ZAI_PROJECT_ROOT`. Один и тот же конфиг будет работать во всех проектах и во всех таких IDE.
- Если такой подстановки нет — укажите в конфиге абсолютный путь к корню проекта; для другого проекта можно завести отдельный конфиг или переопределить env в настройках проекта/workspace.

Идея: **корень проекта задаёт тот, кто запускает MCP (IDE)**, через env — тогда серверу не нужно угадывать, и поведение едино во всех средах.

## Подключение в Cursor

### Вариант: пакет с PyPI (как shadcn — только конфиг)

Установка не нужна: Cursor при старте вызовет `pipx run zai-coding-gateway`, пакет подтянется с PyPI. Но Cursor не видит PATH из терминала, поэтому в `command` нужен **полный путь к pipx**.

1. Установите pipx, если ещё нет: [pypa.github.io/pipx](https://pypa.github.io/pipx/) (`brew install pipx` на macOS, `pip install pipx` и т.п.).
2. Запустите скрипт — он найдёт pipx на вашей платформе и выведет готовый фрагмент для `mcp.json`:
   ```bash
   python scripts/print_mcp_config.py
   ```
   Или из репозитория пакета (после клонирования): `python scripts/print_mcp_config.py`.
3. Скопируйте вывод и вставьте в `~/.cursor/mcp.json` в объект `mcpServers` (если файла нет — создайте с обёрткой `{"mcpServers": { ... } }`).
4. Вставьте в `env` свой `ZAI_API_KEY` и при необходимости `ZAI_PROJECT_ROOT`.
5. Перезапустите MCP в Cursor (или перезапустите Cursor).

Скрипт `print_mcp_config.py` поддерживает macOS, Linux и Windows и подставляет типичные пути к pipx. Если репозитория нет (только пакет с PyPI), подставьте путь к pipx вручную — типичные расположения:

| Платформа | Типичный путь к pipx |
|-----------|----------------------|
| macOS (Homebrew) | `/opt/homebrew/bin/pipx` или `/usr/local/bin/pipx` |
| Linux | `$HOME/.local/bin/pipx` или `/usr/bin/pipx` |
| Windows | `%USERPROFILE%\.local\bin\pipx.exe` или через `where pipx` в cmd |

В терминале: `which pipx` (macOS/Linux) или `where pipx` (Windows) — выведет путь для вашей системы.

### Вариант: локальная разработка (pip install -e .)

1. В проекте: `pip install -e .`
2. В `mcp.json` укажите полный путь к тому же Python, в окружении которого установлен пакет, например:
   ```json
   "command": "/путь/к/.venv/bin/python",
   "args": ["-m", "zai_coding_gateway.main", "--stdio"],
   ```
3. В `env` задайте `ZAI_API_KEY` и при необходимости `ZAI_PROJECT_ROOT`.

## Модели (work / fast)

- Для основного цикла сессии в **solve_in_workspace** (need_more/done/use_tools) используется **work-модель** (по умолчанию **glm-4.7**, контекст 200K).
- Для внутренней консолидации и суммаризации истории в **solve_in_workspace** используется **fast-модель** (по умолчанию **glm-4.5-air**, контекст 128K).
- Выбор модели в вызовах MCP недоступен; при необходимости заменить дефолты задайте **ZAI_WORK_MODEL** и/или **ZAI_FAST_MODEL** в env.

## Запуск

Сервер запускается MCP-клиентом по stdio (Cursor сам стартует процесс по `mcp.json`). Ручной запуск для отладки:

```bash
export ZAI_API_KEY="ваш-ключ"
python -m zai_coding_gateway.main --stdio
```

## Инструменты

Порядок использования: **solve_in_workspace** → **apply_changes** → при необходимости **confirm_session_done**.

### solve_in_workspace

Точка входа: запускает сессию рабочего пространства. Модель получает полный контекст, может запрашивать файлы (need_more), использовать list_dir/grep/glob/read, вернуть отчёт и suggested_changes (в т.ч. предложения по новым файлам). Создание файлов в проекте — только через apply_changes после утверждения архитектором.

**Вход:** `instruction` (строка — формулировка задачи), `todos` (список строк), `file_paths` (список путей относительно корня проекта, обязательный начальный контекст), `max_steps` (число, по умолчанию 10).

**Выход:** `session_id` (строка), `report` (строка), `suggested_changes` (список объектов: каждый с полем `path` и полем `diff` или `content`), `files_used` (список путей).

Лимит: 5 раундов инструментов за сессию. Логи: `workspace_sessions_logs/<session_id>.log` или каталог из `ZAI_SESSION_LOGS_DIR`.

### apply_changes

Применяет утверждённые изменения к проекту. Вызывать после solve_in_workspace, когда принято решение по suggested_changes.

**Вход:** `session_id` (из ответа solve_in_workspace), `changes` (список объектов: каждый `{path: путь к файлу, content: новое содержимое}` или `{path, diff: unified diff или полный текст}`), `confirm_after` (bool, по умолчанию True — после применения очистить сессию). Пути должны входить в сессию.

**Выход:** `session_id`, `applied` (список применённых путей), `cleaned` (bool).

### confirm_session_done

Очищает темп-каталог сессии после принятия решения (после apply_changes или при отказе от изменений).

**Вход:** `session_id`. **Выход:** `session_id`, `cleaned` (true).

## Режим «архитектор + разработчик»

Один поток работы: сформулируйте задачу в **instruction** и **todos**, укажите **file_paths** (начальный контекст — существующие файлы). Запустите **solve_in_workspace** — модель получит контекст, при необходимости запросит ещё файлы или использует list_dir/grep/glob/read, затем вернёт отчёт и suggested_changes (в т.ч. предложения по новым файлам). Все изменения применяются только после вашего утверждения через **apply_changes** (или откажитесь и при необходимости вызовите **confirm_session_done**). Точечное чтение и запись файлов архитектор выполняет средствами IDE.

## Тесты

```bash
pytest tests/ -v
```

Тесты используют мок Z.AI API (реальный ключ не нужен). E2E-тесты с реальным API помечены маркером `e2e` и выполняются только при заданном `ZAI_API_KEY`:

```bash
ZAI_API_KEY=ваш-ключ pytest tests/ -v -m e2e
# или
ZAI_API_KEY=ваш-ключ ./scripts/e2e_smoke.sh
```

**Боевой E2E** (`tests/test_e2e_battle.py`): тестовый проект в `tests/e2e_fixtures/battle_project/`. Тесты проверяют доступ к файлам проекта и solve_in_workspace (структура ответа). Запуск с ключом: `ZAI_API_KEY=... pytest tests/test_e2e_battle.py -v -m e2e`.

В CI по умолчанию e2e не запускают.

## Как проверить, что списывается Coding Plan (а не общий биллинг)

1. **При старте MCP** в stderr выводится строка вида:  
   `zai-coding-gateway: endpoint = https://api.z.ai/api/coding/paas/v4 (Coding Plan)`  
   Если в скобках **Coding Plan** — запросы идут на подписку Coding Plan. Если **общий paas (не Coding Plan)** — используется общий endpoint (без `/coding/` в пути), и списание может идти с общего биллинга.

2. **По умолчанию** (без `ZAI_BASE_URL`) используется `https://api.z.ai/api/coding/paas/v4` — это Coding Plan. Не задавайте `ZAI_BASE_URL`, если хотите использовать только Coding Plan.

3. **Проверка в кабинете Z.AI** — в [использовании/биллинге](https://z.ai) посмотрите, по какому продукту идёт списание (Coding Plan vs общий API).

4. **Кто решает, откуда списывать** — итоговая привязка к Coding Plan или к общему биллингу определяется политикой Z.AI (как правило, по вызываемому endpoint и/или по привязке API-ключа к продукту). Мы со своей стороны гарантируем только то, что запросы уходят на выбранный URL (по умолчанию Coding Plan); ключ вы задаёте в конфиге MCP. При сомнениях — уточните в документации или поддержке Z.AI.

## Диагностика 401 (token expired or incorrect)

Если E2E или вызовы из Cursor возвращают `401 - token expired or incorrect`:

1. **Формат ключа** — Z.AI ожидает ключ вида `{id}.{secret}` и заголовок `Authorization: Bearer <ключ>`. Сервер уже отправляет ключ в этом формате; проверьте, что в `ZAI_API_KEY` нет лишних пробелов и кавычек.
2. **Подписка Coding Plan** — endpoint `api.z.ai/api/coding/paas/v4` доступен только при активной подписке [GLM Coding Plan](https://docs.z.ai/devpack/overview). Убедитесь, что ключ создан для аккаунта с этой подпиской.
3. **Проверка ключа на общем endpoint** — временно задайте `ZAI_BASE_URL=https://api.z.ai/api/paas/v4` и запустите E2E. Если с общим endpoint запрос проходит, а с Coding — 401, значит ключ или аккаунт не привязаны к Coding Plan.
4. **Перевыпуск ключа** — в [управлении API-ключами](https://z.ai/manage-apikey/apikey-list) проверьте, что ключ активен; при необходимости создайте новый и подставьте в `ZAI_API_KEY`.

## Транспорт и развёртывание

Текущий режим — **stdio** (клиент запускает процесс). Развёртывание на удалённом сервере (StreamableHTTP) планируется в следующей итерации.

## Лицензия

MIT.