ИИ и безопасность

MCP для 1С:
как безопасно начать и не открыть лишний доступ

Начнём с маленького MCP-сервера: он умеет только возвращать описание двух учебных объектов 1С. Его можно запустить локально без ключа ИИ, рабочей базы и сетевого порта — и проверить не только ответ, но и отказ.

Схема безопасного доступа помощника к учебной базе через MCP-сервер
  • Понятное разделение частей MCP без словаря на полстраницы
  • Сценарии 1С без доступа к рабочей базе и персональным данным
  • Защита от вредных инструкций внутри документов и ответов
  • Проверки до расширения доступа
Содержание

Как устроен MCP простыми словами

Представьте мастерскую с несколькими закрытыми дверями. Главное приложение решает, какие двери показать нейросети. Клиент MCP держит отдельное соединение, а сервер MCP отдаёт только разрешённые данные, инструкции и инструменты. Нейросеть не должна получать ключ от всего помещения.

Но сам протокол безопасность не создаёт. Любой инструмент MCP нужно воспринимать как программный интерфейс, который может читать данные или выполнять действие. Права проверяются на сервере, входные данные — до выполнения, а важное действие — ещё и самим пользователем.

Безопасные учебные сценарии для 1С

Начните со сценария, где ошибка ничего не меняет: поиск по обезличенной документации проекта, чтение структуры учебной конфигурации или проверка выгруженного модуля без запуска.

  • Найти раздел внутреннего руководства по точному запросу.
  • Вернуть список объектов тестовой конфигурации без содержимого данных.
  • Проверить учебный модуль по локальному списку стандартов.
  • Сформировать план тестирования без автоматического запуска изменений.

Где чаще всего возникает риск

Вредная инструкция может спрятаться не только в сообщении пользователя. Она бывает в документе, комментарии к коду или ответе внешнего сервиса. Модель может принять этот текст за команду. Поэтому содержимое документов не должно расширять права инструмента или отменять подтверждение пользователя.

  • Инструмент получает больше данных или действий, чем нужно для задачи.
  • Секрет попадает в инструкцию модели, журнал или текст ошибки.
  • Модель предлагает изменение, но пользователь не видит его до запуска.
  • Удалённый сервер MCP меняется без фиксации версии и повторной проверки.
  • Ответ одного источника используется как доверенная команда для другого.

Минимальные меры контроля

Права нужно ограничивать кодом на сервере, а не только текстом инструкции для модели. Для каждого инструмента определите владельца, допустимые параметры, источник данных, максимальный объём, время ожидания, запись результата и явно запрещённые действия.

  • Отдельная тестовая учётная запись с минимальными правами.
  • Список конкретных разрешённых методов вместо произвольных команд.
  • Только чтение по умолчанию; изменение — отдельный инструмент и подтверждение.
  • Предварительный показ точного действия и целевого объекта.
  • Удаление ключей доступа, персональных данных и содержимого форм из журналов.
  • Ограничение времени, размера ответа и количества повторов.
  • Фиксация версии сервера и возможность быстро отключить подключение.

Учебный пример: два объекта и одно разрешённое действие

Для первого опыта даже не подключаемся к 1С. В коде лежат два вымышленных описания: справочник «Товары» и документ «Заказ». Сервер получает точное имя и возвращает вид объекта и названия полей. Он не читает записи из базы и не меняет конфигурацию.

Понадобится Node.js 22 или новее. Создайте отдельную учебную папку и сохраните код ниже обычным текстом в файл training-mcp.mjs. Дополнительные пакеты, ключи доступа и настройки рабочей 1С не нужны. На сайте этот код показан как текст и не запускается в вашем браузере.

Это маленький учебный сервер с обменом через стандартный ввод и вывод, без сетевого порта. Он поддерживает начало соединения MCP, просмотр инструмента и его вызов. Полного набора возможностей протокола здесь нет; превращать пример в сетевой сервис без отдельной разработки не нужно.

Учебный сервер целиком: сохраните как training-mcp.mjs
// Учебный MCP по stdio. Node.js 22+, без дополнительных пакетов.
// Не подключается к 1С: возвращает только два описания из этого файла.
const objects = new Map([
  ['Справочник.Товары', { kind: 'Справочник', fields: ['Код', 'Наименование'] }],
  ['Документ.Заказ', { kind: 'Документ', fields: ['Дата', 'Номер', 'Товары'] }],
]);
const tool = {
  name: 'describe_training_object',
  description: 'Описание одного из двух вымышленных учебных объектов 1С',
  inputSchema: {
    type: 'object',
    properties: { name: { type: 'string', enum: [...objects.keys()] } },
    required: ['name'],
    additionalProperties: false,
  },
  annotations: { readOnlyHint: true, openWorldHint: false },
};
const isObject = value => value !== null && typeof value === 'object' && !Array.isArray(value);
let state = 'new';
let messages = 0;
const send = response => process.stdout.write(JSON.stringify(response) + '\n');

function handle(message) {
  const validId = typeof message?.id === 'string' || Number.isSafeInteger(message?.id);
  const id = validId ? message.id : null;
  const fail = (code, text) => send({ jsonrpc: '2.0', id, error: { code, message: text } });
  if (!isObject(message) || message.jsonrpc !== '2.0' || typeof message.method !== 'string'
      || ('id' in message && !validId)) return fail(-32600, 'Некорректный запрос');
  if (!('id' in message)) {
    if (message.method === 'notifications/initialized' && state === 'initialized') state = 'ready';
    return;
  }
  const ok = result => send({ jsonrpc: '2.0', id, result });
  if (message.method === 'ping') return ok({});
  if (message.method === 'initialize') {
    const params = message.params;
    if (state !== 'new' || !isObject(params) || typeof params.protocolVersion !== 'string'
        || !isObject(params.capabilities) || !isObject(params.clientInfo)
        || typeof params.clientInfo.name !== 'string' || typeof params.clientInfo.version !== 'string') {
      return fail(-32602, 'Проверьте параметры начала соединения');
    }
    state = 'initialized';
    return ok({ protocolVersion: '2025-11-25', capabilities: { tools: {} },
      serverInfo: { name: 'training-1c', version: '1.0.0' } });
  }
  if (state !== 'ready') return fail(-32600, 'Сначала завершите начало соединения');
  if (message.method === 'tools/list') return ok({ tools: [tool] });
  if (message.method !== 'tools/call') return fail(-32601, 'Метод не поддерживается');
  if (message.params?.name !== tool.name) return fail(-32602, 'Инструмент не найден');
  const args = message.params?.arguments;
  if (!isObject(args) || Object.keys(args).length !== 1 || !objects.has(args.name)) {
    return ok({ isError: true, content: [{ type: 'text', text: 'Передайте только name из списка учебных объектов' }] });
  }
  return ok({ content: [{ type: 'text', text: JSON.stringify(objects.get(args.name)) }] });
}

// До разбора JSON ограничиваем строку 4096 байтами, сеанс — 100 сообщениями.
// Переполнение завершает процесс без вывода входных данных.
let pending = Buffer.alloc(0);
for await (const chunk of process.stdin) {
  let start = 0;
  for (let index = 0; index < chunk.length; index += 1) {
    if (chunk[index] !== 10) continue;
    if (pending.length + index - start > 4096 || ++messages > 100) process.exit(1);
    const line = Buffer.concat([pending, chunk.subarray(start, index)]).toString('utf8');
    pending = Buffer.alloc(0);
    start = index + 1;
    let message;
    try { message = JSON.parse(line); }
    catch { send({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Некорректный JSON' } }); continue; }
    handle(message);
  }
  if (pending.length + chunk.length - start > 4096) process.exit(1);
  pending = Buffer.concat([pending, chunk.subarray(start)]);
}
if (pending.length) {
  send({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Завершите сообщение переводом строки' } });
}

Как повторить обмен без подключения нейросети

Откройте терминал в учебной папке и запустите команду ниже. Затем вставляйте сообщения по одному, каждое отдельной строкой, нажимая Enter. Это ручная проверка обмена: мы временно выполняем роль приложения, которое обращается к MCP.

После первого сообщения сервер должен вернуть версию протокола 2025-11-25. Второе — уведомление о готовности: ответа на него нет. Третье показывает единственный инструмент и два допустимых имени. Четвёртое возвращает описание справочника.

Запуск из папки с сохранённым файлом
node training-mcp.mjs
Четыре сообщения: каждое отправьте отдельной строкой
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"manual-training","version":"1.0.0"}}}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":2,"method":"tools/list"}
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"describe_training_object","arguments":{"name":"Справочник.Товары"}}}
Содержимое успешного ответа: описание, а не записи справочника
{"kind":"Справочник","fields":["Код","Наименование"]}

Проверьте отказ, а не только красивый ответ

После успешного вызова попробуйте передать путь к файлу вместо имени объекта и добавить второй параметр. В обоих случаях должны прийти признак ошибки и короткое сообщение — без чтения файла, выполнения команды и повторения ваших входных данных.

Автоматическая проверка этого примера запускает тот же файл отдельным процессом: начало соединения, разрешённый вызов, неизвестный объект, лишние параметры, неверный JSON и ограничения размера. Это проверка локального обмена, а не подключения к настоящей базе или испытание конкретной модели ИИ.

Проверка границ учебного примера
ПроверкаОжидаемое поведение
Справочник.ТоварыВид «Справочник», поля «Код» и «Наименование»
Документ.ЗаказВид «Документ», поля «Дата», «Номер» и «Товары»
Путь, неизвестное имя или лишний аргументОтказ: isError = true, никаких данных объекта
Неизвестный инструмент или методОшибка протокола, никаких действий
Строка больше 4 096 байт или сообщение № 101Завершение процесса без вывода переданных данных
Два запрещённых запроса для того же сеанса
{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"describe_training_object","arguments":{"name":"../../private-data"}}}
{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"describe_training_object","arguments":{"name":"Документ.Заказ","command":"изменить документ"}}}

Когда можно расширять сценарий

Расширяйте доступ только после повторяемых проверок, разбора рисков и назначения ответственного. Один удачный запрос ничего не доказывает. Для важных изменений оставляйте подтверждение человеком и отдельно проверяйте каждую новую возможность.

Самопроверка

Частые вопросы

MCP сам защищает данные?

Нет. Протокол задаёт способ взаимодействия, а права, проверку параметров, подтверждение пользователя и запись событий настраивают разработчики приложения и сервера.

Можно ли подключить MCP сразу к рабочей базе?

Для первого запуска — не стоит. Возьмите отдельную учебную среду и выдуманные данные, а риск каждого расширения оценивайте отдельно.

Достаточно ли запретить опасные действия в инструкции для модели?

Нет. Текст помогает направить модель, но настоящая граница безопасности находится в коде, правах и подтверждении пользователя.

Первичные источники

Что проверить дополнительно

·

Полезный материал

Поделиться статьёй

Сохраните материал или отправьте его тому, кому он пригодится.

Telegram ↗ VK ↗

Следующий шаг

Хотите разбирать инструменты ИИ на практике?

Посмотрите содержание программы по ИИ и разберите, как безопасно применять инструменты рядом с основной практикой по 1С.

Открыть программу по ИИ →