5.8.3.3. MCP-сервер (readonly)

Added in version 1.29.0-ms2.

5.8.3.3.1. Обзор

Readonly MCP-сервер — реализация Model Context Protocol поверх сервера приложений Global3, дающая внешнему агенту (LLM-клиенту) доступ к прикладным выборкам (Selection) только на чтение. Сервер выступает MCP-сервером для агента-клиента: агент через свой MCP-транспорт вызывает инструменты (tools), которыми сервер публикует метаданные выборок для discovery и постраничное чтение их строк.

Ключевые свойства:

  • Протокол — JSON-RPC 2.0 поверх транспорта Streamable HTTP (без SSE-стрима).

  • Область (Этап 1, readonly-MVP) — discovery выборок, разрешённых агенту серверной политикой доступа, и headless-чтение их строк. Инструменты записи (set_field, run_operation, commit и т.п.) намеренно исключены.

  • Exclusive Sessioninitialize создаёт монопольный серверный рабочий сеанс (по образцу сервиса es/pkg); каждый вызов инструмента исполняется под монопольным захватом сессии («один tool-call за раз»).

  • Политика доступа — какие выборки видны агенту, определяет серверная политика в секции конфига Configuration.Mcp (см. Политика доступа агента к выборкам).

Внешний путь эндпоинта — /app/sys/rest/es/mcp (см. Справочник URI эндпоинтов).

Attention

MCP-сервер поддерживается только на решениях PostgreSQL (база с секцией eclipseLink в конфиге, режим соединений proxyShared). Oracle-режим не поддерживается: там применяется другой контур работы с БД — соединение захватывается на всё время жизни выборки, а курсор остаётся открытым до её закрытия, поэтому долгоживущие открытые выборки MCP удерживали бы соединения пула.

5.8.3.3.2. Подключение и транспорт

Адрес сервиса

  • http://{server:port}/app/sys/rest/es/mcp

Ресурс смонтирован Jersey-сервлетом /app/sys/rest/*; отдельной настройки web.xml для MCP не требуется — контейнерная аутентификация уже покрывает эту зону.

5.8.3.3.2.1. HTTP-методы

Метод

Назначение

POST

Единая точка входа: диспетчеризация конверта JSON-RPC 2.0 (Content-Type: application/json, Accept: application/json).

DELETE

Закрытие сессии по заголовку Mcp-Session-Id (каскадно закрывает открытые выборки).

GET

Не поддерживается: всегда 405 Method Not Allowed с заголовком Allow: POST, DELETE.

Транспорт — Streamable HTTP: тело запроса и ответа несёт конверт JSON-RPC 2.0, SSE-стрим не используется.

5.8.3.3.2.2. Заголовки запроса

Заголовок

Обяз.

Значение

Authorization

да

Basic <base64(user:password)> или Bearer <jwt> (схема распознаётся регистронезависимо). См. Аутентификация.

Database

нет

Алиас целевой БД/решения; при отсутствии берётся defaultDatabaseAlias из global3.config.xml. Регистр свободный — сервер нормализует имя в UPPER-CASE.

Mcp-Session-Id

для tools/call и DELETE

Идентификатор сессии, выданный сервером в ответе на initialize.

MCP-Protocol-Version

нет

Проверяется на НЕ-initialize-запросах: если задан, но не из поддерживаемого набора — 400. Пустой/отсутствует — берётся дефолт.

Note

Заголовок Database можно слать в любом регистре — сервер приводит имя решения к верхнему регистру. Но для схемы Bearer claim sln внутри JWT сверяется с нормализованным именем побайтно (регистрозависимо), поэтому имя решения в самом токене должно быть уже в ВЕРХНЕМ регистре, иначе 401. См. Аутентификация.

5.8.3.3.2.3. Версии протокола MCP

Версия по умолчанию — 2025-06-18. Поддерживаемый набор (от новой к старой):

  • 2025-06-18

  • 2025-03-26

Согласование: в initialize берётся params.protocolVersion, если он входит в набор, иначе — дефолт; согласованная версия возвращается в result.protocolVersion. На последующих запросах заголовок MCP-Protocol-Version, если присутствует, должен принадлежать этому же набору, иначе ответ — 400 с JSON-RPC-ошибкой Invalid Request.

5.8.3.3.2.4. Методы JSON-RPC и коды ответа

Диспетчер поддерживает ровно пять методов; прочие → METHOD_NOT_FOUND (-32601).

Метод

Назначение и результат

initialize

Запрос (обязан нести id). Согласует версию и возможности, создаёт ES-сессию, возвращает Mcp-Session-Id в заголовке ответа. result: protocolVersion, capabilities.tools.listChanged=false, serverInfo (name: "Global3 AS MCP", version).

notifications/initialized

Нотификация (без id). Ответа нет → HTTP 202.

ping

Запрос. result — пустой объект {}. Сессию не трогает.

tools/list

Запрос. result.tools — массив из 10 дескрипторов {name, description, inputSchema} (JSON Schema draft-07) в стабильном порядке. Сессию не трогает.

tools/call

Запрос. Требует валидный Mcp-Session-Id. params: name (строка) и arguments (объект). Результат — MCP-контент (см. Инструменты).

HTTP-коды ответа

Код

Условие

200 OK

Успешный JSON-RPC-ответ. Важно: протокольные ошибки JSON-RPC (PARSE_ERROR, INVALID_REQUEST, METHOD_NOT_FOUND, INVALID_PARAMS, INTERNAL_ERROR — включая превышение лимита сессий) тоже отдаются с HTTP 200, а тело несёт объект error. Результат инструмента с isError:true — это тоже нормальный 200.

202 Accepted

Принята нотификация (запрос без id); тело пустое.

204 No Content

DELETE нашёл и закрыл сессию.

400 Bad Request

Единственный случай не-200 для JSON-RPC-ошибки: неподдерживаемая MCP-Protocol-Version на НЕ-initialize-запросе.

401 Unauthorized

Нет/невалидные учётные данные; ответ несёт WWW-Authenticate: Bearer.

404 Not Found

tools/call с неизвестным/пустым Mcp-Session-Id либо DELETE несуществующей сессии.

405 Method Not Allowed

GET; ответ несёт Allow: POST, DELETE.

503 Service Unavailable

Сервер в режиме обслуживания (пользователь не SYSTEM); тело text/plain.

5.8.3.3.2.5. Порядок хендшейка

  1. POST initializeAuthorization и Database) → 200; из заголовков ответа клиент запоминает Mcp-Session-Id, из тела — result.protocolVersion/serverInfo.

  2. POST notifications/initialized (без id) → 202.

  3. Рабочие запросы: tools/list (сессия не нужна), tools/call (обязателен Mcp-Session-Id). Каждый tools/call исполняется монопольно.

  4. POST ping — опциональная проверка живости эндпоинта. Сессию не продлевает (исполняется без неё), поэтому от закрытия по простою не спасает.

  5. DELETEMcp-Session-Id) → 204; сессия и её открытые выборки закрываются каскадно.

5.8.3.3.2.6. Пример: initialize

curl -sS -D - "http://localhost:8080/app/sys/rest/es/mcp" \
  -H "Authorization: Bearer $JWT" -H "Database: GSTEST" \
  -H "Content-Type: application/json" -H "Accept: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}'

Ответ — HTTP 200, заголовок Mcp-Session-Id: <uuid>, тело:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-06-18",
    "capabilities": {"tools": {"listChanged": false}},
    "serverInfo": {"name": "Global3 AS MCP", "version": "1.29.0"}
  }
}

Конфигурация MCP-клиента (Streamable HTTP):

{
  "type": "http",
  "url": "http://localhost:8080/app/sys/rest/es/mcp",
  "headers": {"Authorization": "Bearer <jwt>", "Database": "GSTEST"}
}

5.8.3.3.3. Аутентификация

MCP-эндпоинт не содержит собственного кода логина: аутентификацию выполняет транспортный слой (сервлет-фильтр), а ресурс дополнительно делает defense-in-depth (проверяет режим обслуживания и наличие principal) и берёт готовый principal из контейнера. Запрос без валидных учётных данных → 401 с WWW-Authenticate: Bearer.

Эндпоинт принимает обе HTTP-схемы — Basic и Bearer (один фильтр обслуживает обе; источники учётных данных перебираются в порядке: cookie access_token → заголовок Authorization → параметр access_token).

Выбор решения (алиас БД) резолвится единой цепочкой fallback: request-атрибут → заголовок Database → request-параметр DatabasedefaultDatabaseAlias из global3.config.xml. Прежде чем использовать, сервер нормализует имя решения в UPPER-CASE.

Warning

Для схемы Bearer имя решения зашито в JWT как приватный claim sln и сверяется с нормализованным (UPPER-CASE) значением побайтно. Поэтому токен, выпущенный с решением в нижнем регистре (sln="gstest"), даёт 401 — имя БД в токене должно совпадать с нормализованным, то есть быть в ВЕРХНЕМ регистре (sln="GSTEST"). Токены, которые сервер выпускает сам после логина, всегда несут уже заглавное имя решения; проблема воспроизводится для токенов, собранных вне сервера.

Пример «сырого» запроса:

POST /app/sys/rest/es/mcp HTTP/1.1
Authorization: Bearer ast_<jwt...>
Database: GSTEST
MCP-Protocol-Version: 2025-06-18
Content-Type: application/json

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}

Полное описание типов аутентификации — Аутентификация в HTTP сервисах.

5.8.3.3.4. Сессия и её жизненный цикл

MCP-сессия — это серверный рабочий сеанс (Exclusive Session), создаваемый обработкой метода initialize (только запрос с id; initialize без id сессию НЕ создаёт). При создании сервер генерирует случайный UUID и возвращает его клиенту в заголовке Mcp-Session-Id ответа. Внутренний ключ рабочего сеанса детерминирован от тройки (db, user, mcpSessionId) (вид MCP-{db}/{user}[mcp-{mcpSessionId}]); последующие tools/call и DELETE реконструируют его из principal и заголовка Mcp-Session-Id. Сессия строго привязана к тому же principal (db + user).

5.8.3.3.4.1. Открытые выборки

Инструмент open_selection открывает выборку в собственном рабочем контексте, переживающем вызов, и регистрирует её в реестре сессии по handle (UUID), возвращаемому агенту как sessionRef. Инструменты get_opened_meta/read_rows резолвят выборку по sessionRef. Отдельного инструмента close_selection в каталоге НЕТ — открытые выборки закрываются только вместе со всей сессией (каскадно).

Что открытая выборка удерживает до закрытия сессии: сам объект выборки с DataStore и прочитанными строками в памяти сервера, свой WorkSessionContext и объект соединения движка. Физическое соединение с БД при этом НЕ занимается: на PostgreSQL оно берётся из пула на время выполнения запроса и сразу возвращается (короткая транзакция), серверный курсор между вызовами не живёт — строки уже прочитаны в кэш выборки. Поэтому цена открытой выборки измеряется памятью сервера, а не занятыми соединениями пула.

5.8.3.3.4.2. Когда сессия закрывается

Единый путь закрытия каскадно закрывает все открытые выборки сессии (а затем и их контексты). Триггеры:

  1. Явный DELETE с Mcp-Session-Id204. Закрытие идёт под монитором сессии: если параллельно исполняется tools/call, DELETE дождётся его завершения.

  2. Простой по таймауту. Если клиент разорвал HTTP без DELETE, сессию закрывает фоновый поток-уборщик RestSession-Eviction (общий для всех Rest-сессий). Таймаут простоя — 15 минут, период обхода — тоже 15 минут, поэтому фактическая зачистка простаивающей сессии наступает через 15–30 минут после последней активности. Уборщик закрывает только ПРОСТАИВАЮЩИЕ просроченные сессии (не рвёт активный tools/call).

  3. Остановка сервера (contextDestroyed) — закрываются все Rest-сессии.

5.8.3.3.4.3. Лимиты

Лимит

Значение

Поведение при достижении

Открытых выборок на сессию

32

open_selection возвращает ошибку инструмента «Достигнут лимит открытых выборок MCP-сессии (32). Закрой сессию (DELETE) перед открытием новых.»; только что открытая выборка сразу закрывается (ресурсы не текут).

Живых MCP-сессий на пользователя

8

initialize отклоняется JSON-RPC-ошибкой INTERNAL_ERROR (HTTP 200): «Достигнут предел MCP-сессий на пользователя (8)…». Проверка+создание атомарны на per-user-мониторе (защита от TOCTOU).

Оба значения — константы кода (McpSessionAbst, McpRestServiceImpl); конфигом они не настраиваются.

Note

Утечки выборок и их контекстов в штатных путях нет: каждый open_selection завершается либо регистрацией в сессии (гарантированное каскадное закрытие), либо немедленным закрытием на месте (при исчерпании лимита или гонке с закрытием сессии). Брошенная клиентом сессия не живёт вечно — её закроет уборщик простоя за 15–30 минут. Накопление сверху ограничено произведением лимитов (8 × 32).

5.8.3.3.5. Политика доступа агента к выборкам

Какие выборки доступны агенту, решает серверная политика — секция конфига Configuration.Mcp файла global3.config.xml. Это отдельный слой поверх обычной авторизации: даже успешно аутентифицированный агент видит через MCP только разрешённые политикой выборки. Права самого пользователя политика не меняет и строки данных не фильтрует — она сужает перечень выборок, которые агенту вообще можно назвать.

Проверку проходит каждый инструмент, называющий выборку: find_selections ищет только среди разрешённых имён, а адресные инструменты (включая open_selection) отвергают выборку вне перечня ошибкой «Выборка ‘…’ не найдена или недоступна для MCP-discovery».

5.8.3.3.5.1. Два атрибута и правило их чтения

Политика описывается элементом Configuration.Mcp.AgentAccess с двумя атрибутами, и они не равноправны — у каждого своя роль:

  • Configuration.Mcp.AgentAccess.overrideрубильник: переключает весь MCP-контур целиком, не трогая базовую политику. По умолчанию off — «не вмешиваться».

  • Configuration.Mcp.AgentAccess.defaultбазовая политика: ответ сервера, когда рубильник отпущен. По умолчанию deny — «запрещено».

Отсюда правило чтения ровно в два шага:

  1. Сервер смотрит override. Любое значение, кроме off, определяет исход само — default при этом не читается вовсе.

  2. И только при override="off" исход задаёт default.

Что доступно агенту при каждом значении override

override

Агенту доступны

Читается ли default

off (по умолчанию)

при default="deny" — ни одной выборки, при default="allow" — все

да, это единственный такой режим

allowAll

все выборки

нет

denyAll

ни одной выборки

нет

list

только перечисленные в Configuration.Mcp.Selections

нет

«Все выборки» здесь — все существующие в решении (*.avm.xml). Открыть несуществующее имя не даёт ни один режим: в list лишние имена перечня просто ни на что не отображаются, а allowAll открывает ровно то, что в решении есть.

Attention

Конфигурация без секции <mcp> — равно как и пустой <agentAccess> — означает override="off" плюс default="deny": внешний агент не видит ни одной выборки, пока доступ не открыт явно (fail-closed).

Note

Двух атрибутов кажется много там, где исход почти всегда решает один. Они отвечают за разное: default описывает намерение решения («в норме доступ закрыт»), а override — рубильник поверх него.

Положения allowAll и denyAll при этом отладочные: они открывают или закрывают контур целиком, не читая ни default, ни перечень Configuration.Mcp.Selections, — место им на стендах разработки и тестирования (allowAll стоит в dev-конфиге). Рабочий контур на Этапе 1 настраивают перечнем: default="deny" плюс override="list" — так выглядит пример в шаблоне конфига дистрибутива.

5.8.3.3.5.2. Примеры XML-конфигурации

Режим list — рабочая настройка: агенту разрешены ровно перечисленные выборки:

<mcp>
    <agentAccess default="deny" override="list"/>
    <selections>
        <selection name="Gs3_McpOrder"/>
        <selection name="Gs3_McpCustomer"/>
    </selections>
</mcp>

Режим allowAll — отладочный: агенту открыты все выборки решения. Так публикует их dev-конфиг стенда разработки:

<mcp>
    <agentAccess default="deny" override="allowAll"/>
</mcp>

В обоих примерах default="deny" записан для явности намерения, но на исход не влияет: рубильник переведён. Перечень допустимых значений обоих атрибутов — в справочнике XSD: Configuration.Mcp.DefaultAccess и Configuration.Mcp.OverrideMode.

5.8.3.3.6. Инструменты

Каталог публикует ровно 10 readonly-инструментов в стабильном порядке регистрации. Все вызываются методом tools/call (params.name + params.arguments); inputSchema каждого — JSON Schema draft-07.

5.8.3.3.6.1. Упаковка результата и ошибки

Результат любого инструмента — MCP-контент {content:[{type:"text",text}], isError}; полезная нагрузка лежит строкой JSON в content[0].text. Успех — isError:false. Любая прикладная ошибка (неизвестный sessionRef, недоступная выборка, отсутствующий обязательный аргумент, неизвестное имя инструмента) — НЕ протокольная ошибка JSON-RPC, а нормальный result с isError:true и текстом сообщения. Протокольную ошибку INVALID_PARAMS даёт лишь невалидный конверт вызова (отсутствующий/нестроковый name).

Каталог инструментов

Инструмент

Группа

Назначение

find_selections

  1. Discovery

Найти выборки по подстроке в имени/заголовке.

find_in_selection

  1. Discovery

Сквозной поиск операций/атрибутов/полей фильтра по подстроке.

get_selection_meta

  1. Discovery

Узкие метаданные выборки: caption, description, представления (с caption/description/documentation), поля фильтра.

list_attributes

  1. Discovery

Список атрибутов выборки.

get_attribute_meta

  1. Discovery

Статические метаданные одного атрибута.

list_operations

  1. Discovery

Список операций выборки (без служебных).

get_filter_meta

  1. Discovery

Поля фильтра представления (ключи для open_selection.filter).

open_selection

  1. Открытая выборка

Открыть выборку headless, вернуть sessionRef.

get_opened_meta

  1. Открытая выборка

Метаданные открытой выборки с наложенными настройками администрирования.

read_rows

  1. Открытая выборка

Страница строк открытой выборки.

Attention

Расхождение discovery ↔ open: инструменты группы A отдают статическое описание из метаданных выборки, к которому ещё не применены настройки системы администрирования. Поэтому в get_attribute_meta поля visible/readOnly приходят нейтральными заглушками (жёстко true/false), а fieldType у прикладных сущностных атрибутов — ftUnknown: реальный тип движок разворачивает лишь при активации выборки. Значения, отражающие открытую выборку — с наложенными настройками администрирования и развёрнутым типом поля, — даёт ТОЛЬКО get_opened_meta.

5.8.3.3.6.2. Группа A. Discovery без открытия выборки

Семь инструментов читают статические и рантайм-метаданные разрешённых агенту выборок (политика доступа) без открытия выборки — каждый работает под своим короткоживущим контекстом с соединением, освобождаемым по выходу из вызова. Типичный маршрут: find_selectionsget_selection_metalist_attributes/list_operations/get_attribute_meta/get_filter_meta → переход к open_selection.

5.8.3.3.6.2.1. find_selections

  • Назначение: найти доступные агенту выборки по подстроке в имени ИЛИ заголовке.

  • Аргументы: query (string, обязателен).

  • Результат: {selections:[{name, caption, module}], truncatedResults?, truncatedCaptionSearch?, note?}.

  • Ограничения/флаги: список усечён верхним пределом 200 результатов (truncatedResults:true); поиск по caption ограничен бюджетом 500 загрузок (truncatedCaptionSearch:true). Каждый флаг сопровождается человекочитаемым note. При флаге клиент должен сузить query или указать точное имя.

  • Ошибки: пустой query → «Не задан обязательный аргумент ‘query’».

5.8.3.3.6.2.2. find_in_selection

  • Назначение: поиск операций и/или атрибутов (и полей фильтра) по подстроке — по всем доступным выборкам или в одной.

  • Аргументы: query (string, обязателен); selection (string, опц.) — имя выборки; scope (string enum, опц.) — строго operation | attribute.

  • Результат: {matches:[{selection, kind, name, caption, description}], truncated?, note?}; kind ∈ {operation, attribute, filter} (filter — только когда scope не задан).

  • Ограничения/флаги: без selection обход ограничен 200 просмотренными выборками, совпадений — не более 500; достижение любого лимита → truncated:true.

  • Ошибки: пустой query; недопустимый scope → «Недопустимый scope ‘…’. Допустимые значения: operation, attribute (или не задавать).»

5.8.3.3.6.2.3. get_selection_meta

  • Назначение: метаданные выборки — caption, description (человекочитаемое описание выборки из <documentation> уровня view), представления (с caption/description/ documentation) и поля фильтра (список атрибутов НЕ возвращает — для него list_attributes).

  • Аргументы: name (string, обязателен).

  • Результат: {caption, description, representations:[{name, caption, description, documentation}], detailSelections:[string], filters:[{name, caption, fieldType, defaultValue, editorType}]}. Тексты представлений (caption/description/ documentation) могут быть null, если соответствующий узел метаданных не заведён. Замечание: detailSelections лёгким путём не резолвятся — всегда пустой список.

  • Ограничения: при недоступности живых метаданных деградирует до минимальной нагрузки (caption равен имени, description = null, representations и прочие списки пусты) без падения.

  • Ошибки: пустой name; выборка не разрешена политикой доступа.

5.8.3.3.6.2.4. list_attributes

  • Назначение: список атрибутов выборки (статические discovery-метаданные).

  • Аргументы: name (string, обязателен).

  • Результат: {attributes:[{name, caption, description}]}.

  • Ошибки: пустой name; выборка не разрешена политикой доступа.

5.8.3.3.6.2.5. get_attribute_meta

  • Назначение: статические метаданные одного атрибута.

  • Аргументы: name (string, обязателен) — имя выборки; attr (string, обязателен) — имя атрибута.

  • Результат: {name, caption, fieldType, visible, readOnly, required}.

  • Ограничения: visible всегда true, readOnly всегда false — заглушки, НЕ реальные права (см. get_opened_meta); fieldType часто ftUnknown.

  • Ошибки: пустой name/attr; атрибут не найден → «Атрибут ‘<attr>’ не найден в выборке ‘<name>’.»; выборка не разрешена политикой доступа.

5.8.3.3.6.2.6. list_operations

  • Назначение: список операций выборки (служебные исключены).

  • Аргументы: name (string, обязателен).

  • Результат: {operations:[{name, caption, description}]}.

  • Ошибки: пустой name; выборка не разрешена политикой доступа.

5.8.3.3.6.2.7. get_filter_meta

  • Назначение: поля фильтра представления; их имена — КЛЮЧИ объекта filter в open_selection.

  • Аргументы: name (string, обязателен).

  • Результат: {filters:[{name, caption, fieldType, defaultValue, editorType}]}.

  • Ограничения: при недоступности метаданных фильтра деградирует до пустого списка без падения. Для detail-представлений ключ мастера — super$id.

  • Ошибки: пустой name; выборка не разрешена политикой доступа.

5.8.3.3.6.3. Группа B. Работа по открытой выборке

Три инструмента работают ТОЛЬКО по уже открытой выборке, адресуемой sessionRef. Жизненный цикл и лимиты — см. Сессия и её жизненный цикл.

5.8.3.3.6.3.1. open_selection

  • Назначение: открыть выборку headless на чтение и вернуть sessionRef.

  • Аргументы: name (string, обязателен); representation (string, опц.) — имя представления (по умолчанию основное); filter (object, опц.) — свободный объект имя_поля→значение (additionalProperties:true). Имена полей filter берутся из get_filter_meta (например flt_dOrderDateFrom); для detail-представлений ключ мастера — super$id.

  • Результат: {sessionRef, name, representation}.

  • Ограничения: регистрация в сессии с лимитом 32 открытых выборок; открытие выборки, не разрешённой политикой доступа, запрещено.

  • Ошибки: пустой name; выборка не разрешена политикой доступа; превышение лимита 32; ошибка активации/сборки выборки (как isError, ресурсы освобождаются).

5.8.3.3.6.3.2. get_opened_meta

  • Назначение: прочитать метаданные открытой выборки — поля и операции с наложенными настройками системы администрирования.

  • Аргументы: sessionRef (string, обязателен).

  • Результат: {name, representation, caption, attributes:[{name, caption, fieldType, visible, readOnly, required}], operations:[{name, caption, description, visible, enabled}]}. У атрибутов visible/readOnly — значения системы администрирования (Attribute.isAdminVisible/isAdminReadOnly), fieldType — развёрнутый тип живой выборки. У операций visible/enabled — настройки администрирования вместе с прикладной логикой (isVisible() && isAdminVisible(), isEnabled() && isAdminEnabled()).

  • Ошибки: пустой sessionRef; неизвестный/закрытый sessionRef → «Неизвестный sessionRef: <ref>» / «Открытая выборка не найдена по ссылке ‘…’ (не открыта или уже закрыта).»

5.8.3.3.6.3.3. read_rows

  • Назначение: прочитать страницу строк открытой выборки. Фильтр задаётся ТОЛЬКО в open_selection — повторная фильтрация не поддерживается.

  • Аргументы: sessionRef (string, обязателен); page (object, опц.) — {offset, limit}. По умолчанию offset=0, limit=100.

  • Результат: {columns:[string], rows:[{колонка→значение}], offset, loadedCount, allFetched}.

  • Ограничения: limit ограничивается сверху значением 1000 (защита от вычитывания всей таблицы); отрицательные offset/limit нормализуются к 0; читается только запрошенная страница. Пагинация: увеличивать offset, пока allFetched=false; allFetched=true означает конец данных.

  • Ошибки: пустой sessionRef; неизвестный sessionRef (те же два слоя, что у get_opened_meta).

5.8.3.3.7. Типовой сценарий

Discovery → открытие заказа → чтение строк → lookup клиента (на фикстурах Gs3_McpOrder/Gs3_McpCustomer). Заголовки во всех запросах: Authorization, Database, Content-Type: application/json, а для tools/call — ещё Mcp-Session-Id (из ответа на initialize).

1. Найти выборку заказов (find_selections):

{"jsonrpc":"2.0","id":3,"method":"tools/call",
 "params":{"name":"find_selections","arguments":{"query":"Gs3_McpOrder"}}}

Ответ (нагрузка — строкой JSON в content[0].text):

{"jsonrpc":"2.0","id":3,"result":{
  "content":[{"type":"text",
    "text":"{\"selections\":[{\"name\":\"Gs3_McpOrder\",\"caption\":\"Заказы\",\"module\":\"gs3\"}]}"}],
  "isError":false}}

2. Открыть выборку в списочном представлении (open_selection) — возвращает sessionRef:

{"jsonrpc":"2.0","id":4,"method":"tools/call",
 "params":{"name":"open_selection",
   "arguments":{"name":"Gs3_McpOrder","representation":"List"}}}
{"jsonrpc":"2.0","id":4,"result":{
  "content":[{"type":"text",
    "text":"{\"sessionRef\":\"a1b2c3d4-...\",\"name\":\"Gs3_McpOrder\",\"representation\":\"List\"}"}],
  "isError":false}}

3. Прочитать первую страницу строк (read_rows) по полученному sessionRef:

{"jsonrpc":"2.0","id":5,"method":"tools/call",
 "params":{"name":"read_rows",
   "arguments":{"sessionRef":"a1b2c3d4-...","page":{"offset":0,"limit":100}}}}

В ответе content[0].text несёт {columns, rows, offset, loadedCount, allFetched}. Если allFetched=false — продолжать, увеличивая offset.

4. Lookup клиента: взять из строки заказа идентификатор клиента, открыть Gs3_McpCustomer (шаг open_selection с filter по нужному полю, имена полей берутся из get_filter_meta) и прочитать его строку через read_rows.

5. Завершить диалогDELETE с Mcp-Session-Id204 (открытые выборки закрываются каскадно).

5.8.3.3.8. За рамками Этапа 1

Этап 1 намеренно ограничен чтением и грубой серверной политикой доступа. Ниже перечислено то, чего в нём нет, — чтобы отсутствие не выглядело недоработкой.

Note

Состав и сроки следующего этапа не зафиксированы, имена настроек и аннотаций в этом разделе рабочие и могут измениться. Раздел описывает направление, а не принятые решения.

5.8.3.3.8.1. Запись

Инструментов записи (set_field, set_param, run_operation, run_validate, commit) в каталоге нет. Дело не в правах и не в headless: открытым остаётся вопрос транзакционной границы. insert/edit/установка значений копят черновик в памяти DataStore, фиксация идёт отдельной операцией — и между двумя вызовами инструментов агент может оставить наполовину заполненный объект или частично зафиксированные данные. Этап записи начинается с прототипа именно этой границы, а не с добавления инструментов.

5.8.3.3.8.2. Права: декларация на уровне выборки

Сегодня доступ агента задаётся только серверной секцией <mcp>: она одна на весь контур и ничего не знает о конкретной выборке. Прорабатывается отдельная ось «доступно агенту» — декларативная характеристика в самом прикладном коде: признак на классе выборки плюс свойство операции, наследуемое от выборки по умолчанию. Смысл оси в том, что права пользователя не отличают «человек осознанно нажал кнопку» от «модель решила вызвать операцию», и решать это должен прикладной разработчик, а не только администратор контура.

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

5.8.3.3.8.3. Описания для агента

Качество работы агента упирается в описания: у многих выборок description пуст, а у атрибутов роль описания играет заголовок. Поэтому Этап 1 отдаёт ровно то, что уже есть в коде — @Oper и заголовки avm/dvm. Отдельный слой описаний, написанных специально для агента (в том числе примеры значений), в рантайм MCP не загружается и остаётся предметом проработки.

See also