Фича: инструкция по работе с MCP-сервером в системном промпте агента #180

Closed
opened 2026-06-25 00:24:34 +03:00 by Ghost · 0 comments

Что

Дать администратору возможность задать для каждого внешнего MCP-сервера свободный текст-инструкцию («как и когда пользоваться инструментами этого сервера»), который агент получает в системном промпте — рядом с описаниями инструментов.

Это перенос уже существующей в кодовой базе идеи SERVER_INSTRUCTIONS (встроенный MCP-пакет отдаёт её внешним клиентам при initialize, см. packages/mcp/src/index.ts) на внешние MCP-серверы, настраиваемые внутри приложения.

Зачем

Сейчас агент видит только per-tool .description из AI-SDK Tool. Серверного гайда «как пользоваться набором инструментов» в системном промпте нет вообще. Для нетривиальных серверов (выбор инструмента по интенту, порядок вызовов, краевые случаи) этого недостаточно.

Принятые решения

  • Источник текста: пишет администратор (доверенный текст, как системный промпт). Авто-захват client.instructions из хендшейка сервера НЕ берём — это недоверенный внешний контент и риск prompt-injection.
  • Гранулярность: одна инструкция на сервер (как SERVER_INSTRUCTIONS).
  • Охват: только внешние MCP-серверы. Встроенные инструменты Docmost остаются с текущими per-tool описаниями.

Архитектура (сквозной срез)

Новое поле — обычный несекретный текст, поэтому, в отличие от headersEnc, оно возвращается во view и форму.

  1. Миграция: nullable-колонка instructions text в ai_mcp_servers.
  2. Тип таблицы apps/server/src/database/types/ai-mcp-servers.types.ts: instructions: string | null.
  3. Репозиторий ai-mcp-server.repo.ts: insert/update обрабатывают instructions (пустое/пробельное → null).
  4. DTO create/update: @IsOptional() @IsString() @MaxLength(4000) instructions?: string (кап на объём промпта/токены; встроенный гайд ~1.5KB).
  5. Сервис + view mcp-servers.service.ts: instructions в McpServerView/toView, проброс в create/update с нормализацией blank→null.
  6. Сборка тулсета mcp-clients.service.ts: новый тип McpServerInstruction { serverName, toolPrefix, instructions }, проброс через ExternalToolset/CacheEntry/buildEntry/toolsFor. Инструкция добавляется только для серверов, которые подключились И дали ≥1 инструмент (после аллоулиста).
  7. Системный промпт ai-chat.prompt.ts: новый параметр mcpInstructions + рендер блока <mcp_tooling> внутри сэндвича безопасности (после context, перед завершающим SAFETY_FRAMEWORK). Заголовок секции указывает неймспейс-префикс инструментов сервера (например, tavily_*).
  8. Вызывающий код ai-chat.service.ts: важно — переставить порядок: сейчас buildSystemPrompt вызывается раньше toolsFor. Нужно сначала собрать внешний тулсет, потом строить промпт и пробросить external.instructions. В дефолтный external добавить instructions: [].
  9. Клиент: instructions в типах (ai-mcp-server-service.ts), Textarea в форме (ai-mcp-server-form.tsx) с подсказкой про неймспейс-префикс; i18n-ключи (en/ru).

Безопасность и краевые случаи

  • Текст доверенный (уровень системного промпта) → размещается в сэндвиче безопасности, не может переопределить правила. Анти-инъекция для результатов инструментов (SAFETY_FRAMEWORK) не затрагивается.
  • Гайд недоступного/выключенного сервера или сервера с полностью отфильтрованным аллоулистом в промпт не попадает.
  • Неймспейс-коллизии: префикс в хедере = sanitizeName(name); при редких коллизиях отдельные тулы получают суффиксы — гайд остаётся ориентировочным (это руководство, не контракт).
  • Кэш тулсета уже инвалидируется на любой CRUD-мутации → правка инструкции подхватывается на следующем ходу.
  • public-share chat использует отдельный buildShareSystemPrompt без внешних MCP — вне области изменений.

Тесты

  • ai-chat.prompt.spec.ts: блок рендерится при наличии инструкций (имя/префикс/текст); не рендерится при пустом списке/тексте; стоит между context и завершающим SAFETY; обе копии SAFETY на месте.
  • mcp-servers-to-view.spec.ts: instructions во view; blank→null.
  • mock-тест mcp-clients: buildEntry собирает инструкцию только для подключённого сервера с непустым текстом и ≥1 тулом.
  • repo-spec: round-trip insert/update; blank→null.
  • DTO: срабатывает MaxLength.
  • ai-chat.service spec: системный промпт содержит внешние инструкции; порядок toolsFor→buildSystemPrompt не сломан.

Чек-лист реализации

  • Миграция: nullable-колонка instructions
  • Тип таблицы ai-mcp-servers.types.ts
  • Репозиторий insert/update
  • DTO create/update + MaxLength(4000)
  • Сервис + McpServerView/toView + нормализация blank→null
  • mcp-clients: McpServerInstruction + проброс через тулсет
  • ai-chat.prompt: mcpInstructions + buildMcpToolingBlock
  • ai-chat.service: переставить порядок (тулсет → промпт), пробросить инструкции
  • Клиент: типы + Textarea + i18n (en/ru)
  • Тесты по всем слоям
## Что Дать администратору возможность задать для каждого внешнего MCP-сервера свободный текст-инструкцию («как и когда пользоваться инструментами этого сервера»), который агент получает **в системном промпте** — рядом с описаниями инструментов. Это перенос уже существующей в кодовой базе идеи `SERVER_INSTRUCTIONS` (встроенный MCP-пакет отдаёт её внешним клиентам при `initialize`, см. `packages/mcp/src/index.ts`) на внешние MCP-серверы, настраиваемые внутри приложения. ## Зачем Сейчас агент видит только per-tool `.description` из AI-SDK `Tool`. Серверного гайда «как пользоваться набором инструментов» в системном промпте нет вообще. Для нетривиальных серверов (выбор инструмента по интенту, порядок вызовов, краевые случаи) этого недостаточно. ## Принятые решения - **Источник текста:** пишет администратор (доверенный текст, как системный промпт). Авто-захват `client.instructions` из хендшейка сервера НЕ берём — это недоверенный внешний контент и риск prompt-injection. - **Гранулярность:** одна инструкция на сервер (как `SERVER_INSTRUCTIONS`). - **Охват:** только внешние MCP-серверы. Встроенные инструменты Docmost остаются с текущими per-tool описаниями. ## Архитектура (сквозной срез) Новое поле — обычный несекретный текст, поэтому, в отличие от `headersEnc`, оно **возвращается** во view и форму. 1. **Миграция:** nullable-колонка `instructions text` в `ai_mcp_servers`. 2. **Тип таблицы** `apps/server/src/database/types/ai-mcp-servers.types.ts`: `instructions: string | null`. 3. **Репозиторий** `ai-mcp-server.repo.ts`: insert/update обрабатывают `instructions` (пустое/пробельное → `null`). 4. **DTO** create/update: `@IsOptional() @IsString() @MaxLength(4000) instructions?: string` (кап на объём промпта/токены; встроенный гайд ~1.5KB). 5. **Сервис + view** `mcp-servers.service.ts`: `instructions` в `McpServerView`/`toView`, проброс в create/update с нормализацией blank→null. 6. **Сборка тулсета** `mcp-clients.service.ts`: новый тип `McpServerInstruction { serverName, toolPrefix, instructions }`, проброс через `ExternalToolset`/`CacheEntry`/`buildEntry`/`toolsFor`. Инструкция добавляется **только** для серверов, которые подключились И дали ≥1 инструмент (после аллоулиста). 7. **Системный промпт** `ai-chat.prompt.ts`: новый параметр `mcpInstructions` + рендер блока `<mcp_tooling>` внутри сэндвича безопасности (после `context`, перед завершающим `SAFETY_FRAMEWORK`). Заголовок секции указывает неймспейс-префикс инструментов сервера (например, `tavily_*`). 8. **Вызывающий код** `ai-chat.service.ts`: **важно** — переставить порядок: сейчас `buildSystemPrompt` вызывается раньше `toolsFor`. Нужно сначала собрать внешний тулсет, потом строить промпт и пробросить `external.instructions`. В дефолтный `external` добавить `instructions: []`. 9. **Клиент:** `instructions` в типах (`ai-mcp-server-service.ts`), `Textarea` в форме (`ai-mcp-server-form.tsx`) с подсказкой про неймспейс-префикс; i18n-ключи (en/ru). ## Безопасность и краевые случаи - Текст доверенный (уровень системного промпта) → размещается в сэндвиче безопасности, не может переопределить правила. Анти-инъекция для **результатов** инструментов (`SAFETY_FRAMEWORK`) не затрагивается. - Гайд недоступного/выключенного сервера или сервера с полностью отфильтрованным аллоулистом в промпт не попадает. - Неймспейс-коллизии: префикс в хедере = `sanitizeName(name)`; при редких коллизиях отдельные тулы получают суффиксы — гайд остаётся ориентировочным (это руководство, не контракт). - Кэш тулсета уже инвалидируется на любой CRUD-мутации → правка инструкции подхватывается на следующем ходу. - public-share chat использует отдельный `buildShareSystemPrompt` без внешних MCP — вне области изменений. ## Тесты - `ai-chat.prompt.spec.ts`: блок рендерится при наличии инструкций (имя/префикс/текст); не рендерится при пустом списке/тексте; стоит между `context` и завершающим `SAFETY`; обе копии `SAFETY` на месте. - `mcp-servers-to-view.spec.ts`: `instructions` во view; blank→null. - mock-тест `mcp-clients`: `buildEntry` собирает инструкцию только для подключённого сервера с непустым текстом и ≥1 тулом. - repo-spec: round-trip insert/update; blank→null. - DTO: срабатывает `MaxLength`. - `ai-chat.service` spec: системный промпт содержит внешние инструкции; порядок toolsFor→buildSystemPrompt не сломан. ## Чек-лист реализации - [ ] Миграция: nullable-колонка `instructions` - [ ] Тип таблицы `ai-mcp-servers.types.ts` - [ ] Репозиторий insert/update - [ ] DTO create/update + `MaxLength(4000)` - [ ] Сервис + `McpServerView`/`toView` + нормализация blank→null - [ ] `mcp-clients`: `McpServerInstruction` + проброс через тулсет - [ ] `ai-chat.prompt`: `mcpInstructions` + `buildMcpToolingBlock` - [ ] `ai-chat.service`: переставить порядок (тулсет → промпт), пробросить инструкции - [ ] Клиент: типы + `Textarea` + i18n (en/ru) - [ ] Тесты по всем слоям
Ghost closed this issue 2026-06-25 12:49:15 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: vvzvlad/gitmost#180