1C MCP Proxy
by Diversus23
README.md
# MCP-прокси сервер для «Управление IT-отделом 8», редакция 4.0
## Что это
Прокси-сервер между MCP-клиентами (Claude Desktop, Cursor) и 1С:Предприятие. Транслирует MCP-протокол в JSON-RPC вызовы к HTTP-сервису 1С.
Предназначен для конфигурации [«Управление IT-отделом 8»](https://softonit.ru/catalog/products/it/) — встроенный в неё MCP-сервис предоставляет AI-агентам инструменты для работы с задачами, проектами, базой знаний, файлами и лентой уведомлений.
**Возможности:**
- Два транспорта: stdio (для нативных клиентов) и Streamable HTTP (для веб)
- Проксирование всех MCP-примитивов: Tools, Resources, Prompts
- Опциональная OAuth2 авторизация с per-user креденшилами
## Документация
Пошаговые руководства для решения «Управление IT-отделом 8», редакция 4.0:
- [Развертывание MCP-сервера на виртуальной машине](https://docs.softonit.ru/it/iskusstvennyy-intellekt/mcp/razvertyvanie-mcp-servera) — установка прокси на отдельной ВМ с Docker.
- [Подключение AI-клиентов к MCP-серверу](https://docs.softonit.ru/it/iskusstvennyy-intellekt/mcp/podklyuchenie-ai-klientov) — настройка Claude Desktop, Cursor и Claude Code, в том числе с OAuth2-авторизацией.
Ниже — техническая справка по самому прокси.
## Быстрый старт
### Требования
- **Node.js 22.19+**
- 1С:Предприятие 8.3.24+ с опубликованным HTTP-сервисом
### Установка
```bash
# Установка зависимостей
npm install
# Сборка
npm run build
```
### Выбор режима работы
> 📖 Пошаговая настройка клиентов (Claude Desktop, Cursor, Claude Code) с OAuth2-авторизацией — в статье [Подключение AI-клиентов](https://docs.softonit.ru/it/iskusstvennyy-intellekt/mcp/podklyuchenie-ai-klientov).
#### Stdio режим
Для локальных MCP-клиентов (Claude Desktop, Cursor).
Настройки указываются в конфигурации клиента через переменные окружения.
**Минимальная конфигурация клиента:**
```json
{
"mcpServers": {
"uit": {
"command": "node",
"args": ["dist/index.js"],
"cwd": "/path/to/mcp",
"env": {
"MCP_ONEC_URL": "http://localhost/base",
"MCP_ONEC_USERNAME": "admin",
"MCP_ONEC_PASSWORD": "password"
}
}
}
}
```
#### HTTP режим
Для веб-приложений и множественных клиентов.
Настройки указываются в файле `.env` в корне проекта или через переменные окружения:
```bash
# Скопируйте пример
cp .env.example .env
```
**Минимальный .env:**
```ini
MCP_ONEC_URL=http://localhost/base
MCP_ONEC_USERNAME=admin
MCP_ONEC_PASSWORD=password
```
**Запуск:**
```bash
node dist/index.js http --port 8000
```
**Подключение клиентов с нативной поддержкой HTTP** (Cursor, VS Code, Claude Code):
```json
{
"mcpServers": {
"uit": {
"type": "http",
"url": "http://localhost:8000/mcp"
}
}
}
```
**Подключение Claude Desktop к HTTP-серверу**
`claude_desktop_config.json` не поддерживает поле `"url"` (`"type": "http"`) — только stdio-запуск через `"command"`. Поэтому Claude Desktop подключается к HTTP-серверу через локальный мост [`mcp-remote`](https://www.npmjs.com/package/mcp-remote): он стартует по stdio и проксирует запросы на HTTP-endpoint. Нужен установленный Node.js (см. [Требования](#требования)) — его вызывает `npx`.
```json
{
"mcpServers": {
"uit": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://192.168.88.124:8000/mcp", "--allow-http"]
}
}
}
```
Замените `http://192.168.88.124:8000/mcp` на адрес вашего HTTP-сервера (для локального запуска — `http://localhost:8000/mcp`). Флаг `--allow-http` обязателен, если endpoint работает по обычному HTTP, а не HTTPS.
### Docker
Запуск в контейнере для изоляции и упрощения развертывания.
> 📖 Полное пошаговое руководство по развёртыванию на отдельной ВМ — [Развертывание MCP-сервера](https://docs.softonit.ru/it/iskusstvennyy-intellekt/mcp/razvertyvanie-mcp-servera).
#### Первичная установка
```bash
# 1. Клонируем репозиторий
git clone https://github.com/Diversus23/itmcp.git
# 2. Переходим в папку, где находится клонированный itmcp
cd itmcp
# 3. Скопировать конфигурацию
cp .env.docker.example .env
# 4. Отредактировать .env (обязательно: MCP_ONEC_URL, MCP_ONEC_USERNAME, MCP_ONEC_PASSWORD)
# 5. Запустить через docker compose
docker compose build --no-cache
docker compose up -d
# 6. Проверка, что все работает
curl http://localhost:8000/health
```
**Или напрямую через Docker:**
```bash
# Сборка образа
docker build -t 1c-mcp-proxy .
# Запуск с переменными окружения
docker run -d \
-p 8000:8000 \
-e MCP_ONEC_URL=http://host.docker.internal/base \
-e MCP_ONEC_USERNAME=admin \
-e MCP_ONEC_PASSWORD=password \
--name mcp-proxy \
1c-mcp-proxy
```
**Важно про сеть:**
- Если 1С на **том же хосте**: используйте `host.docker.internal` (Mac/Windows) или IP хоста `172.17.0.1` (Linux) вместо `localhost`
- Если 1С на **другом сервере**: указывайте его реальный адрес как обычно
**Логи:**
```bash
docker compose logs -f
```
**Остановка:**
```bash
docker compose down
```
#### Обновление в docker
Когда выходит новая версия необходимо пересобрать образ MCP в docker. Необходимо перейти в папку, куда клонировали репозиторий `itmcp`.
```bash
# 1. Получаем изменения новой версии
git pull
# 2. Останавливаем контейер
docker compose down
# 3. Пересобираем образ
docker compose build --no-cache
# 4. Запускаем новый образ (обновленный)
docker compose up -d
```
## Режимы работы
### Stdio режим
- Общение через stdin/stdout
- Используется локальными MCP-клиентами
- Логи идут в stderr
- OAuth2 не поддерживается (при `MCP_AUTH_MODE=oauth2` запуск завершится ошибкой)
### HTTP режим
**Endpoints:**
- `/mcp` - Streamable HTTP транспорт
- `/health` - проверка состояния
- `/info` - информация о сервере
- `/` - список endpoints
**Проверка работы:**
```bash
curl http://localhost:8000/health
```
## Режимы авторизации
### Без OAuth2 (по умолчанию)
```bash
MCP_AUTH_MODE=none # по умолчанию
```
**Поведение:**
- Все обращения к 1С выполняются от одного пользователя
- Креденшилы задаются в конфигурации: `MCP_ONEC_USERNAME` и `MCP_ONEC_PASSWORD`
- Используется Basic Auth для всех запросов к 1С
### С OAuth2
```bash
MCP_AUTH_MODE=oauth2
MCP_PUBLIC_URL=http://your-server:8000
```
**Поведение:**
- OAuth2 доступен только в HTTP режиме (в stdio запуск завершится ошибкой)
- Каждый клиент авторизуется своими креденшилами 1С
- Креденшилы передаются через OAuth2 flow
- `MCP_ONEC_USERNAME` и `MCP_ONEC_PASSWORD` не используются (если заданы, будут проигнорированы)
**Поддерживаемые OAuth2 flows:**
- **Password Grant** - передача username/password напрямую
- **Authorization Code + PKCE** - авторизация через HTML-форму
- **Dynamic Client Registration** - автоматическая регистрация клиентов
**Дополнительные endpoints (для OAuth2):**
- `/.well-known/oauth-protected-resource` - Protected Resource Metadata
- `/.well-known/oauth-authorization-server` - Authorization Server Metadata
- `/register` - регистрация клиентов
- `/authorize` - HTML форма авторизации
- `/token` - получение/обновление токенов
## Конфигурация
Все настройки задаются через переменные окружения с префиксом `MCP_` или через CLI аргументы.
### Подключение к 1С
| Переменная | Описание | По умолчанию | Обязательная |
|------------|----------|--------------|--------------|
| `MCP_ONEC_URL` | URL базы 1С | - | Всегда |
| `MCP_ONEC_USERNAME` | Имя пользователя | - | При `AUTH_MODE=none` |
| `MCP_ONEC_PASSWORD` | Пароль | - | При `AUTH_MODE=none` |
| `MCP_ONEC_SERVICE_ROOT` | Корень HTTP-сервиса | `mcp` | Нет |
### HTTP-сервер
| Переменная | Описание | По умолчанию | Обязательная |
|------------|----------|--------------|--------------|
| `MCP_HOST` | Хост для прослушивания | `127.0.0.1` | Нет |
| `MCP_PORT` | Порт | `8000` | Нет |
| `MCP_CORS_ORIGINS` | CORS origins (JSON array) | `["*"]` | Нет |
### MCP
| Переменная | Описание | По умолчанию | Обязательная |
|------------|----------|--------------|--------------|
| `MCP_SERVER_NAME` | Имя сервера | `Управление IT-отделом 8 MCP` | Нет |
| `MCP_SERVER_VERSION` | Версия | `1.0.0` | Нет |
| `MCP_SESSION_TTL_MS` | TTL HTTP-сессии `/mcp` без активности (мс); сессия с открытым SSE-стримом не истекает | `86400000` (24 ч) | Нет |
| `MCP_MAX_SESSIONS` | Максимум одновременных HTTP-сессий `/mcp`; при превышении вытесняется самая старая неактивная | `1000` | Нет |
| `MCP_ALLOWED_HOSTS` | Белый список `Host`-заголовков (без порта), защита от DNS rebinding; пусто — проверка отключена | (не задан) | Нет |
| `MCP_SAVE_FILE_DIR` | Белый каталог для `save_file`; в HTTP-режиме без настройки — служебный каталог во временной директории, в stdio — без ограничений | (не задан) | Нет |
| `MCP_LOG_LEVEL` | Уровень логирования | `INFO` | Нет |
Допустимые уровни: `DEBUG`, `INFO`, `WARNING`, `ERROR`
### OAuth2
| Переменная | Описание | По умолчанию | Обязательная |
|------------|----------|--------------|--------------|
| `MCP_AUTH_MODE` | Режим: `none` или `oauth2` | `none` | Нет |
| `MCP_PUBLIC_URL` | Публичный URL прокси | (определяется из запроса) | При `AUTH_MODE=oauth2` |
| `MCP_OAUTH2_CODE_TTL` | TTL authorization code (сек) | `120` | Нет |
| `MCP_OAUTH2_ACCESS_TTL` | TTL access token (сек) | `3600` | Нет |
| `MCP_OAUTH2_REFRESH_TTL` | TTL refresh token (сек) | `1209600` | Нет |
| `MCP_OAUTH2_STORE_PATH` | Путь к JSON-снапшоту токенов | (только в памяти; в Docker-образе — `/app/data/oauth2.json`) | Нет |
| `MCP_OAUTH2_REFRESH_GRACE_MS` | Окно идемпотентности refresh-rotation (мс) | `300000` | Нет |
| `MCP_OAUTH2_REVOKE_ON_REUSE` | Строгий режим: отзыв всей цепочки токенов при позднем повторном refresh | `false` | Нет |
| `MCP_AUTH_RATE_LIMIT_MAX_ATTEMPTS` | Неудачных попыток входа до блокировки; `0` — отключить | `5` | Нет |
| `MCP_AUTH_RATE_LIMIT_WINDOW_MS` | Окно накопления неудачных попыток (мс) | `900000` | Нет |
| `MCP_AUTH_RATE_LIMIT_BLOCK_MS` | Базовая длительность блокировки (мс) | `60000` | Нет |
| `MCP_AUTH_RATE_LIMIT_BY_IP` | Учитывать IP при подсчёте неудач; `false`/`0` — только по логину | `true` | Нет |
| `MCP_TRUST_PROXY` | Express `trust proxy`: `true`, число хопов или строка | (не задан) | За reverse proxy |
**Персистентность токенов.** При указанном `MCP_OAUTH2_STORE_PATH` access- и
refresh-токены сериализуются в JSON-файл (атомарная запись через временный
файл) каждые 30 секунд и при graceful shutdown. После рестарта прокси
загружает снапшот, отбрасывая истекшие записи. Это устраняет повторные
логины пользователей при перезапуске контейнера / краше процесса.
> **⚠️ Безопасность снапшота.** Файл содержит логины и пароли пользователей
> 1С в открытом виде (Basic Auth требуется для каждого запроса к 1С).
> Файл создаётся с правами `0o600` (только владелец) на POSIX-системах;
> на Windows mode игнорируется — обеспечьте ограниченные NTFS ACL на
> каталог. Не храните снапшот в общем томе, не коммитьте в репозиторий
> (`./data` уже игнорируется через `.gitignore`).
> **⚠️ Права каталога в Docker.** Процесс в контейнере работает под
> пользователем `node` (uid 1000). При bind-mount каталога хоста
> (`./data:/app/data`), созданного root'ом, запись снапшота будет падать
> с `EACCES` — прокси сообщит об этом `ERROR`-строкой при старте, а каждый
> перезапуск контейнера будет разлогинивать всех клиентов. Используйте
> именованный том (как в `docker-compose.yml`) или выполните на хосте
> `chown -R 1000:1000 ./data`.
**Refresh-rotation и параллельные сессии.** Повторное использование одного
и того же `refresh_token` — штатная ситуация для MCP-клиентов с несколькими
одновременными сессиями: несколько окон Claude Code делят один файл
креденшилов и при истечении access-токена все разом идут обновлять одну и
ту же пару, а опоздавшие приходят со старым токеном спустя минуты и часы.
Поэтому по умолчанию (лояльный режим) сервер хранит использованные
refresh-токены, пока жив выданный при ротации access-токен, и на повторное
использование идемпотентно возвращает актуальную пару, догоняя опоздавшую
сессию по всей цепочке ротаций — без реавторизации и без последствий для
остальных сессий. `MCP_OAUTH2_REFRESH_GRACE_MS` задаёт «горячее» окно
идемпотентности (используется в строгом режиме и как нижняя граница
хранения).
Строгий режим `MCP_OAUTH2_REVOKE_ON_REUSE=true`: повторное использование
после grace-window считается компрометацией и отзывает всю цепочку токенов
(RFC 6819 §5.2.2.3). Включайте его, только если каждый пользователь работает
строго из одного процесса-клиента: при параллельных сессиях он приводит к
внезапному массовому разлогину («MCP server requires re-authorization»).
**Защита от перебора паролей.** Эндпоинты `/authorize` и `/token`
(password grant) считают неудачные попытки входа отдельно по IP клиента и
по логину 1С. После `MCP_AUTH_RATE_LIMIT_MAX_ATTEMPTS` неудач в окне
`MCP_AUTH_RATE_LIMIT_WINDOW_MS` ключ блокируется на
`MCP_AUTH_RATE_LIMIT_BLOCK_MS`; каждая следующая серия неудач удваивает
блокировку (экспоненциальный backoff, потолок — 1 час). Заблокированный
запрос получает `429 Too Many Requests` с заголовком `Retry-After`, не
доходя до 1С. Успешный вход сбрасывает счётчик. Ошибки соединения с 1С
неудачными попытками не считаются.
> **⚠️ За reverse proxy.** Если прокси работает за nginx/traefik, задайте
> `MCP_TRUST_PROXY` (например, `1` — число прокси-хопов), иначе все запросы
> будут видны с IP прокси: блокировка по IP заблокирует всех пользователей
> сразу. Дополнительно рекомендуется включить платформенную защиту от
> подбора паролей в самой 1С (платформа 8.3.21+).
> **Общий IP у всех клиентов.** В окружениях, где реальный IP клиента
> недоступен и все запросы видны с одного адреса (Docker Desktop на
> Windows/Mac, прокси без `X-Forwarded-For`), блокировка по IP срабатывает
> на всех сразу. В этом случае задайте `MCP_AUTH_RATE_LIMIT_BY_IP=false` —
> останется независимая блокировка по логину.
### CLI аргументы
Переопределяют переменные окружения:
```bash
node dist/index.js http \
--onec-url http://server/base \
--onec-username admin \
--onec-password secret \
--auth-mode oauth2 \
--public-url http://proxy:8000 \
--port 8000 \
--log-level DEBUG
```
Полный список аргументов:
```bash
node dist/index.js --help
```
## Архитектура
### Общая схема
```
+------------------+
| MCP Client | (Claude Desktop, Cursor)
| (stdio/HTTP) |
+--------+---------+
| MCP Protocol
v
+--------------------+
| Node.js Proxy |
| - mcp-proxy | Проксирование MCP -> JSON-RPC
| - http-server | Express + Streamable HTTP + OAuth2
| - stdio-server | Stdio транспорт
| - onec-client | HTTP-клиент для 1С
+--------+-----------+
| JSON-RPC over HTTP
| Basic Auth (username:password)
v
+--------------------+
| 1C HTTP Service | /hs/mcp/rpc
| МСP-подсистема | 39 tools, resources, prompts
+--------------------+
```
### Модули
- **`index.ts`** - точка входа
- **`main.ts`** - CLI парсинг и запуск
- **`config.ts`** - конфигурация через Zod
- **`mcp-proxy.ts`** - ядро MCP-сервера (проксирование)
- **`onec-client.ts`** - HTTP-клиент для 1С
- **`http-server.ts`** - Express + Streamable HTTP + OAuth2
- **`stdio-server.ts`** - stdio транспорт
- **`auth/oauth2.ts`** - OAuth2 авторизация (Store + Service)
- **`logger.ts`** - логирование в stderr
### Проксирование MCP-примитивов
Все MCP-запросы транслируются в JSON-RPC к 1С:
**Tools (инструменты):**
- `tools/list` -> список доступных инструментов
- `tools/call` -> вызов инструмента с аргументами
**Resources (ресурсы):**
- `resources/list` -> список доступных ресурсов
- `resources/read` -> чтение содержимого ресурса
**Prompts (промпты):**
- `prompts/list` -> список доступных промптов
- `prompts/get` -> получение промпта с параметрами
## Интеграция с 1С
Прокси ожидает HTTP-сервис в 1С по адресу:
```
{MCP_ONEC_URL}/hs/{MCP_ONEC_SERVICE_ROOT}/
```
Например: `http://localhost/base/hs/mcp/`
### Endpoints 1С
1. **`GET /health`**
- Проверка доступности сервиса
- Ответ: `{"status": "ok"}`
- Используется для валидации креденшилов в OAuth2
2. **`POST /rpc`**
- JSON-RPC endpoint для всех MCP-операций
- Content-Type: `application/json`
- Basic Auth: `username:password`
### Формат JSON-RPC запроса
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {}
}
```
### Формат JSON-RPC ответа
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "get_metadata",
"description": "Получить метаданные объекта",
"inputSchema": {}
}
]
}
}
```
## Разработка
```bash
# Установка зависимостей (заодно ставит git-хуки через husky)
npm install
# Сборка
npm run build
# Сборка в watch-режиме
npm run dev
# Запуск stdio
npm start
# Запуск HTTP
npm run start:http
```
### Контроль качества
```bash
npm run typecheck # проверка типов без сборки
npm run lint # ESLint
npm run lint:fix # ESLint с автоисправлением
npm run format # Prettier: отформатировать
npm run format:check # Prettier: только проверка
npm test # запуск тестов (Vitest)
npm run test:watch # тесты в watch-режиме
npm run test:coverage # тесты с отчётом о покрытии
npm run check # format:check + lint + typecheck + test (как в CI)
```
Перед каждым коммитом `husky` + `lint-staged` автоматически прогоняют ESLint
и Prettier по изменённым файлам. CI (GitHub Actions) дополнительно проверяет
сборку на Node 22 и 24, собирает Docker-образ и запускает CodeQL.
История изменений — в [CHANGELOG.md](CHANGELOG.md).
---
**MIT License**
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues