b1-mcp-server
Безопасный сервер Model Context Protocol, который предоставляет AI-клиенту (например, Claude Code) контролируемый доступ к SAP Business One через базу данных (HANA / MS SQL) и API Service Layer на основе OData.
Возможности
-
7 инструментов MCP:
connect_database,execute_sql,execute_service_layer(GET),execute_service_layer_write(PATCH / POST / DELETE),execute_service_layer_write_unattended(те же операции записи без подтверждения; только для профилей разработки),get_schema_info,check_connection. -
Несколько сред: можно переключаться между базами данных клиентов во время работы сервера, используя именованные профили подключений. Перезапуск не требуется.
-
Только чтение по умолчанию: путь выполнения SQL принимает только
SELECT. Все операцииINSERT/UPDATE/DELETE/DROP/CREATE/ALTER/EXECблокируются на стороне сервера без возможности обойти запрет через подтверждение. Операции записи передаются человеку для выполнения в реальном клиенте базы данных. -
Защитные механизмы на стороне сервера: все SQL-запросы, переданные в
execute_sql, проходят одинаковую проверку — независимо от того, составил их пользователь или AI. Анонимные блокиDO BEGIN..ENDклассифицируются по самой опасной содержащейся в них инструкции. Поэтому запись, скрытая за завершающимSELECT, всё равно распознаётся как операция записи. -
Журнал аудита: попытки подключения с выбранным профилем, а также вызовы базы данных и Service Layer, дошедшие до этапа проверки политики или выполнения, записываются в формате JSON Lines, включая отклонённые политикой запросы.
-
Ограничение частоты вызовов для каждого инструмента с использованием скользящего окна.
Установка
Требуется Node.js версии 20 или новее.
npx b1-mcp-server
Либо добавьте сервер в конфигурацию MCP для Claude Code:
{
"mcpServers": {
"b1-db": {
"command": "npx",
"args": ["-y", "b1-mcp-server"]
}
}
}
Зависимости
Пять зависимостей времени выполнения: @modelcontextprotocol/sdk, zod, @sap/hana-client и mssql, а также generic-pool, который используется для пула подключений HANA. Для доступа к базе данных применяется локальный драйвер src/db/directDb.ts, построенный непосредственно на клиентах HANA и MS SQL. Запросы Service Layer проходят через адаптер со строгой проверкой TLS в src/sl/serviceLayerAdapter.ts, который использует встроенные в Node fetch и https. HTTP-фреймворк, axios и драйвер PostgreSQL не используются.
При чистой установке проверка показывает found 0 vulnerabilities без блока overrides. Это также означает, что запуск npx -y b1-mcp-server проходит без проблем: overrides там не применяются, поскольку npx запускается без корневого package.json.
Профили подключений
При запуске сервер не подключён ни к одной базе данных. Профили загружаются из ~/.claude/connections.json (по одному профилю для каждой среды). Во время работы AI выбирает профиль с помощью инструмента connect_database.
Правила поиска профиля в connect_database:
-
В первую очередь выполняется точное совпадение по
idилиdbName. -
Частичные совпадения принимаются, только если им соответствует один профиль.
-
Если частичному совпадению соответствуют несколько профилей, запрос отклоняется и выводится список подходящих вариантов.
Пример файла ~/.claude/connections.json:
[
{
"id": "client_a_prd",
"dbType": "hana",
"dbServer": "10.0.0.10:30015",
"dbName": "SBO_CLIENT_A",
"dbUser": "B1ADMIN",
"dbPassword": "•••",
"slUrl": "https://10.0.0.10:50000/b1s/v2",
"slUser": "manager",
"slPassword": "•••",
"slAllowedMethods": ["GET", "PATCH", "POST"]
},
{
"id": "client_b_hmg",
"dbType": "mssql",
"dbServer": "10.0.0.20:1433",
"dbName": "SBO_CLIENT_B_HMG",
"dbUser": "SA",
"dbPassword": "•••"
}
]
Для каждого профиля обязательны поля id, dbType и dbName. Чтобы настроить DirectDb, добавьте dbServer и dbUser; чтобы настроить Service Layer — slUrl и slUser.
Для каждой настроенной стороны также требуется непустой пароль (dbPassword или slPassword). Если пароль отсутствует или пуст, подключение завершается локально без попытки входа. Это позволяет избежать лишней ошибки, которая по настроенной политике входа может учитываться при блокировке учётной записи. Непустые значения пароля передаются без удаления пробелов в начале и конце. В одном профиле можно настроить одну или обе стороны.
Необязательное поле slAllowedMethods задаёт HTTP-методы, которые инструменты Service Layer могут использовать для этого профиля: любые из GET, PATCH, POST, DELETE (регистр не учитывается). Если поле не указано, профиль доступен только для чтения (["GET"]); каждый метод записи необходимо явно добавить в список. Метод PUT не поддерживается: он заменяет сущность целиком и удаляет все поля, отсутствующие в теле запроса. Если значение некорректно (неизвестный метод, пустой список или значение не является массивом), профиль пропускается — сервер не пытается угадать намерение. Список применяется при подключении и выводится инструментом connect_database; после его изменения следующий вызов connect_database повторно выполнит вход в Service Layer.
Разрешение метода записи не отменяет требование подтверждения: для каждой операции записи по-прежнему необходимо отдельное подтверждение человека, как описано в разделе о безопасности.
Необязательное поле slWriteApproval определяет, кто подтверждает операцию записи. Значение "elicitation" (по умолчанию): сервер запрашивает подтверждение через форму MCP. Значение "client": сервер не отправляет форму и полагается на запрос разрешения со стороны MCP-клиента. Этот режим предназначен для конфигураций, в которых форма недоступна пользователю, например Claude Code Remote Control, который пересылает запросы разрешения, но не формы MCP. Режим client действует только тогда, когда MCP-клиент представляется как Claude Code; для любого другого клиента используется elicitation. Сервер не может проверить, был ли показан запрос разрешения, поэтому надёжность этого режима зависит от настроек разрешений клиента: добавьте mcp____execute_service_layer_write в permissions.ask, но не в allow, и не используйте этот режим с --dangerously-skip-permissions. Иначе ответ «Yes, don't ask again» на первый запрос сохранит правило allow, после чего все последующие операции записи будут выполняться без запроса. Для производственных профилей используйте elicitation. Инструмент connect_database выводит режим, действующий для текущего клиента; после его изменения следующий вызов connect_database повторно выполнит вход в Service Layer.
Значение "none" предназначено для сред разработки, где запрос подтверждения перед каждой операцией записи замедляет работу. В режиме client инструмент execute_service_layer_write работает как обычно, а профиль также открывает доступ к execute_service_layer_write_unattended, который выполняет те же операции записи без какого-либо подтверждения и доступен любому MCP-клиенту. Этот инструмент отклоняет запросы для всех профилей, у которых не задано значение "none". Поэтому можно добавить mcp____execute_service_layer_write_unattended в permissions.allow, а обычный инструмент записи оставить в permissions.ask: для профилей разработки операции будут выполняться без запроса, а для всех остальных профилей подтверждение по-прежнему потребуется. Разрешение задаётся только в файле подключений, поэтому его безопасность зависит от защиты этого файла: никогда не устанавливайте "none" для профиля, подключённого к производственным данным.
Поддерживаются оба корневых пути SAP Service Layer: /b1s/v1 для OData v3 и /b1s/v2 для OData v4. Вход в систему, проверки работоспособности и относительные запросы к Service Layer используют точный корневой путь, заданный в профиле. Проверка запросов не зависит от версии: вызывающая сторона передаёт только относительную конечную точку, например Items?$select=ItemCode&$top=1. За различия в запросах и полезной нагрузке OData отвечает вызывающая сторона; настройте корневой путь, поддерживаемый целевой установкой SAP B1. Начиная с SAP Business One 10.0 FP 2405 компания SAP объявила OData v3 устаревшим и сделала OData v4 основным протоколом. Поэтому, если целевая установка поддерживает /b1s/v2, предпочтительно использовать его. Подробнее см. в справочнике по API Service Layer.
Модель безопасности
Сервер не подставляет пароли профилей в ответы инструментов MCP или записи аудита намеренно. Серверный процесс считывает пароли из ~/.claude/connections.json и напрямую передаёт их настроенной базе данных или конечной точке Service Layer для входа. После этого адаптер Service Layer сохраняет только cookie сессии, но не имя пользователя и пароль. Сообщения об ошибках драйвера базы данных и Service Layer возвращаются и записываются в аудит без изменений для диагностики. Поэтому произвольный текст ошибок от вышестоящих систем не проходит через универсальный механизм маскировки секретов. Не указывайте секреты в идентификаторах профилей, именах баз данных, URL и других метаданных, не предназначенных для паролей. Эта защита на уровне протокола не помешает AI-хосту с неограниченным доступом к оболочке или файловой системе от имени того же пользователя открыть файл напрямую. См. рекомендации по настройке hook ниже.
По умолчанию для подключений к Service Layer требуется HTTPS с обычной проверкой сертификата. Если используется внутренняя инфраструктура сертификатов, перед запуском процесса MCP укажите путь к пакету сертификатов CA в NODE_EXTRA_CA_CERTS.
Для устаревшей установки SAP, сертификат которой невозможно обновить, при первом вызове connect_database выполняется проверка TLS без отправки учётных данных, после чего через запрос подтверждения с помощью формы MCP запрашивается явное одобрение. Принятый сертификат сохраняется по каноническому источнику https://host:port в ~/.claude/service-layer-trust.json (режим 0600), поэтому профили, использующие одну конечную точку SL, используют один и тот же закреплённый сертификат. Файл создаётся после явного одобрения или успешного переноса совпадающего закреплённого сертификата из устаревшего профиля с ручными настройками и не содержит учётных данных. При изменении сертификата требуется повторное одобрение; клиенты без поддержки запросов подтверждения отказываются от подключения. Устаревшие поля ручной настройки сертификата в профилях принимаются только для однократного переноса в это локальное хранилище.
После закрепления сертификата проверяется только его точный отпечаток — проверка имени хоста дополнительно не выполняется. SAP B1 выдаёт самоподписанный сертификат Service Layer для короткого имени компьютера (DNS:MYSERVER), тогда как клиенты обычно подключаются по FQDN. Повторная проверка имени отклонила бы уже одобренный сертификат, не оставив возможности принять его. Поэтому slTlsServerName задаёт только имя SNI, передаваемое во время рукопожатия, для хостов, которые предоставляют разные сертификаты в зависимости от имени. Для подключения по FQDN с закреплённым сертификатом этот параметр не нужен. URL Service Layer могут содержать DNS-имена, IPv4- или IPv6-адреса. Для URL с IP-адресом SNI не передаётся, если только в slTlsServerName не указано DNS-имя; устаревшее значение slTlsServerName в виде IP-адреса принимается, но не влияет на работу. Старые записи хранилища сертификатов могут по-прежнему содержать serverName в качестве совместимого резервного значения, однако новую настройку SNI следует задавать в профиле подключения, а не в метаданных доверенного сертификата.
Профили перезагружаются при каждом вызове connect_database. Пока подключение к базе данных активно, в профиль, настроенный только для БД, можно добавить slUrl, slUser и slPassword; при следующем вызове подключение к БД сохранится, а инициализирован будет только Service Layer.
Полное отключение проверки сертификатов не поддерживается: адаптер отказывается подключаться, если задано NODE_TLS_REJECT_UNAUTHORIZED=0.
При вызове connect_database со значением "list" AI видит:
-
идентификатор профиля
id -
dbName,dbType -
флаг доступных возможностей (
DB,SL,DB+SL) -
признак
active, если в данный момент подключена хотя бы одна из настроенных сторон
Рекомендации по настройке connections.json:
-
Ограничьте доступ к файлу своей учётной записью.
-
Linux/macOS:
chmod 600 ~/.claude/connections.json. -
Windows:
icacls "%USERPROFILE%\.claude\connections.json" /inheritance:r /grant:r "%USERNAME%:F".
-
-
Не добавляйте этот файл в какой-либо git-репозиторий и не синхронизируйте его через облачные сервисы.
-
В POSIX-системах сервер отказывается загружать файл профилей, если он предоставляет какие-либо права группе или другим пользователям.
-
Каждый пользователь должен использовать собственную копию файла. Учётные данные никогда не включаются в этот npm-пакет.
Рекомендации по настройке hook и песочницы Claude Code
Используйте централизованно управляемый hook Claude Code PreToolUse или аналогичную политику хоста, чтобы запрещать запросы AI к каноническому пути connections.json с учётом разрешения символических ссылок. Правило должно охватывать все инструменты, способные работать с файловой системой (Read, Grep, Glob, инструменты редактирования и записи, а также файловые серверы MCP), а не только точные совпадения с именем файла. С командами оболочки всё сложнее: недостаточно искать текст пути, поскольку команды, рабочие каталоги и символические ссылки позволяют обращаться к файлу косвенно. Предпочтительно использовать песочницу файловой системы хоста, запрещающую доступ к файлу с учётными данными; если это невозможно, требуйте подтверждения для доступа из оболочки за пределами рабочей области.
Hook или политика должны находиться вне проекта и пользовательских настроек, доступных для изменения AI. В корпоративных развёртываниях Claude Code устанавливайте их в управляемые платформой настройки, чтобы инструкции проекта не могли их переопределить. Запретите AI запись в service-layer-trust.json; чтение этого файла не раскрывает секреты, но его целостность определяет, какому устаревшему сертификату доверяют.
Hooks обеспечивают дополнительный уровень защиты, но не являются границей безопасности ОС. Они регулируют вызовы инструментов, выполняемые через AI-хост, но не могут помешать этому процессу MCP, другому локальному процессу или вредоносному npm-пакету, запущенному от имени того же пользователя ОС, прочитать файл с режимом 0600. Для надёжной изоляции требуется отдельная учётная запись ОС, песочница или удалённый брокер учётных данных. См. рекомендации по hooks Claude Code и настройкам, управляемым сервером.
Настройка (переменные среды)
Все переменные необязательны.
Файла .env нет. Сервер напрямую считывает process.env и никогда не загружает dotenv-файл, поэтому файл .env, помещённый рядом с сервером, ничего не изменит. Для MCP-сервера, работающего через stdio, окружение задаёт клиент: укажите переменные в блоке env конфигурации MCP, и клиент передаст их запускаемому процессу:
{
"mcpServers": {
"b1-db": {
"command": "npx",
"args": ["-y", "b1-mcp-server"],
"env": {
"MCP_QUERY_TIMEOUT_MS": "120000",
"MCP_SL_WRITES_ENABLED": "false"
}
}
}
}
Учётные данные не настраиваются таким способом: они хранятся только в connections.json. Путь к файлу определяется относительно домашнего каталога, поэтому сервер найдёт его независимо от того, из какого каталога запущен npx.
| Переменная | По умолчанию | Назначение |
|---|---|---|
MCP_CONNECTIONS_FILE | ~/.claude/connections.json | Путь к файлу профилей |
MCP_AUDIT_LOG_PATH | ~/.claude/logs/b1-mcp-audit.jsonl | Файл журнала аудита в формате JSONL. Если переменная не задана или пуста, используется путь по умолчанию |
MCP_LOG_LEVEL | info | Подробность вывода в stderr: debug / info / warn / error. Запись в файл JSONL не зависит от этой настройки |
MCP_MAX_QUERY_LENGTH | 8000 | Максимальная длина SQL в символах |
MCP_RATE_LIMIT_MAX_CALLS | 60 | Максимальное число вызовов каждого инструмента за период |
MCP_RATE_LIMIT_WINDOW_MS | 60000 | Длительность периода ограничения частоты запросов |
MCP_QUERY_TIMEOUT_MS | 60000 | Предельное время выполнения запроса. Если запрос продолжает выполняться по истечении этого времени, он прерывается. Увеличьте значение, если для корректного формирования отчёта требуется больше времени |
MCP_SL_TIMEOUT_MS | 30000 | Тайм-аут входа в Service Layer или выполнения запроса |
MCP_SL_TRUST_FILE | ~/.claude/service-layer-trust.json | Локальное хранилище доверенных сертификатов, не содержащее секретов |
MCP_SL_MAX_URL_LENGTH | 2048 | Максимальная длина относительного URL OData |
MCP_SL_MAX_BODY_CHARS | 50000 | Максимальная длина сериализованного тела запроса PATCH/POST |
MCP_SL_WRITES_ENABLED | true | Аварийный переключатель для отключения всех операций записи Service Layer (PATCH, POST, DELETE). Значения true/false не зависят от регистра; любое недопустимое значение отключает запись. Заменяет MCP_SL_PATCH_ENABLED, которое больше не считывается. Однако любое значение, отличное от true, в переменной со старым именем по-прежнему отключает запись, поэтому обновление не включит её повторно |
MCP_ELICITATION_TIMEOUT_MS | 120000 | Время, отведённое человеку на ответ в форме подтверждения (для доверия к сертификату или записи в Service Layer); по истечении этого времени операция завершается отказом |
MCP_MAX_RESULT_ROWS | 500 | Максимальное число строк, возвращаемых модели; лишние строки отсекаются, и об этом сообщается |
MCP_MAX_RESULT_CHARS | 100000 | Максимальное число символов в JSON с результатами; ограничение применяется после ограничения числа строк |
MCP_DRY_RUN | false | Точное значение true позволяет проверять исходный SQL и операции записи в Service Layer без их выполнения; подключения и запросы GET к Service Layer при этом выполняются |
Все указанные выше числовые ограничения должны быть положительными целыми числами. Если переменная не задана или содержит только пробельные символы, используется значение по умолчанию. Любое другое недопустимое значение, включая 0, отрицательное число или нецелое число, приводит к сообщению о резервном значении в stderr и использованию значения по умолчанию: ошибочная настройка ограничения никогда не означает «без ограничений». Для MCP_LOG_LEVEL также используется info, если переменная не задана или пуста; при любом другом недопустимом значении сообщается о переходе к резервному значению.
Краткое описание защитных ограничений
Путь выполнения SQL работает только для чтения, поэтому результат запроса не зависит от того, к какой таблице вы обращаетесь:
| Операция | Любая таблица (основные таблицы SAP, UDT, пользовательские, временные) |
|---|---|
| SELECT | Разрешён с учётом ограничений, специфичных для SELECT, перечисленных ниже |
| INSERT / UPDATE / DELETE / DROP | Заблокированы — выполните операцию самостоятельно в клиенте БД |
| CREATE / ALTER | Заблокированы |
| EXEC / EXECUTE / CALL | Заблокированы — через этот сервер нельзя запускать хранимые процедуры |
Также блокируются запросы с несколькими операторами (точки с запятой вне BEGIN..END), незакрытыми кавычками или скобками, продвижением последовательностей (NEXTVAL / NEXT VALUE FOR), OPENQUERY / OPENROWSET / OPENDATASOURCE, SELECT ... INTO и подсказками блокировок SQL Server с высоким уровнем воздействия (UPDLOCK, XLOCK, TABLOCK, TABLOCKX, HOLDLOCK, SERIALIZABLE, REPEATABLEREAD).
Для запросов Service Layer разрешены только методы, указанные в профиле в slAllowedMethods (по умолчанию — только GET); PUT недоступен. Для PATCH и DELETE требуется endpoint сущности с прямой адресацией по ключу. POST принимает набор сущностей, операцию сервиса или один action для сущности с ключом (Orders(12)/Close); при записи параметры запроса не допускаются. Операции записи выполняются через отдельный инструмент execute_service_layer_write. Это позволяет клиенту запрашивать подтверждение для операций записи по имени инструмента, не запрашивая его для GET. В каждой операции записи необходимо указывать целевую database; она должна совпадать с базой данных подключённого Service Layer.
По умолчанию для каждой операции записи требуется явное подтверждение пользователя через форму MCP. Экран подтверждения привязан к базе данных, корневому URL Service Layer, endpoint, точному содержимому запроса, списку полей и хешу тела запроса SHA-256. Клиент без поддержки elicitation не может выполнять запись. В профиле с slWriteApproval: "client" форма заменяется запросом разрешения Claude Code. Профиль с slWriteApproval: "none" также разрешает неподтверждённую запись через execute_service_layer_write_unattended (см. описание полей профиля выше).
При MCP_DRY_RUN=true операции записи никогда не выполняются, а MCP_SL_WRITES_ENABLED=false глобально отключает все операции записи.
Модель классификации таблиц по типам (SAP_CORE / SAP_USER / CUSTOM / TEMP) с правилами для каждой операции по-прежнему существует в src/guardrails/rules/ и полностью покрыта тестами, но в работающей версии сервера, доступной только для чтения, она не задействована.
Полное описание модели и известных ограничений см. в SECURITY.md.
Навык агента
В файле skills/b1-mcp-server/SKILL.md описано, как AI-агенту пользоваться этими инструментами: сначала установить подключение, разобраться в причинах отказов и способах их устранения, понять, почему при тайм-ауте HANA сообщается об отключённом сокете, как выглядят усечённые результаты и как подтверждаются операции записи Service Layer. Чтобы установить навык в Claude Code, скопируйте папку в ~/.claude/skills/. Другие агенты, которые читают файлы SKILL.md, используют для этого собственную папку навыков.
Разработка
npm install
npm run build
npm run test
npm run test:watch
# Optional aliases (same behavior, useful if you prefer explicit naming):
npm run test:bash
npm run test:watch:bash
Только для исходного кода из репозитория можно выполнить быструю проверку подключения, указав профиль из connections.json (scripts/ не входит в npm tarball):
npx tsx scripts/test-connection.ts <profile-id>
Лицензия
MIT — см. LICENSE.