[feat][mcp] Убрать math и автолинк голых доменов из MCP-markdown-записи (без флагов; формулы — через JSON) + text-режим чтения для машинного диффа #502

Open
opened 2026-07-11 05:09:35 +03:00 by agent_vscode · 0 comments
Collaborator

Суть

Две зеркальные боли агентского фидбека: markdown-слой при машинной работе искажает данные.

Запись: ($ticket_lifetime=2592000) превращается в LaTeX-формулу, gitea.vvzvlad.xyz авто-линкуется. Чтение под дифф: авто-ссылки и склейки строк дают ложные расхождения.

По коду виновник записи — не markdown как таковой (CommonMark foo_bar_baz не курсивит), а два расширения поверх: кастомный $...$-регексп (math-inline.ts) канонического импортера и автолинк бессхемных доменов. «_ → нижний индекс» — это KaTeX уже внутри ошибочно созданной math-ноды.

Решение по итогам обсуждения (зафиксировано владельцем):

  • math и автолинк голых доменов в MCP-markdown-записи отключаются совсем, без флагов и opt-in. Кому нужна формула — update_page_json с mathInline/mathBlock-нодой: JSON-путь и существует для сложных случаев.
  • Первоначальный вариант «plain-режим в обход парсера целиком» отклонён: отбирает у агента структуру (заголовки/списки/фенсы), создаёт второй канон производства PM (проект осознанно держит один — см. «no second canon» в markdown-fragment.ts и боль #293).

Решение

1. Запись: параметризовать канонический импортер, MCP зовёт с выключенными расширениями

markdownToProseMirror(md, { parseMath: false, fuzzyLinkify: false }):

  • parseMath: false$...$-регексп не применяется, доллары остаются текстом;
  • fuzzyLinkify: false — автолинк только явных https://…-URL; голые домены — текст; [text](url) работает как всегда.

MCP-инструменты (createPage, updatePageMarkdown, markdown-фрагменты insert/patch) зовут импортер ТОЛЬКО так — без флагов в контракте инструментов. Другие потребители импортера (полный импорт файлов, git-sync) — дефолты не меняются (там markdown пишут люди/экспортёры, math легитимен).

В описания write-инструментов добавить: «литеральные блоки (конфиги, shell) — оборачивай в код-фенсы; формулы — через update_page_json».

2. Чтение: format: "text" у getPage

Плоский детерминированный текст узлов для машинного диффа: инлайн-марки/якоря/автоформат отброшены, блок = строка(и), hardBreak = \n (после #503). Реализация — переиспользовать серверный jsonToText-путь (питает tsvector), не изобретать второй сериализатор. Стабильность вывода между версиями пакета = контракт, закрепляется snapshot-тестом.

Крайние случаи

  • Существующие агентские markdown-записи с $...$: перестанут создавать формулы — это и есть цель (владелец: формулы через JSON). Человеческий редактор и импорт файлов не затронуты.
  • Round-trip export→import (export_page_markdown, lossless из #328): экспорт может содержать $…$-текст из старых формул? Нет — экспорт эмитит math-ноды своей разметкой, а ИМПОРТ полных файлов идёт с дефолтами (math on) — lossless-пара не рвётся. Зафиксировать тестом.
  • Не-текстовые ноды в text-режиме: детерминированные плейсхолдеры ([image], [table 3x4]) — стабильно и задокументировано в описании инструмента.
  • git-sync использует тот же импортер — его вызовы НЕ трогаем (дефолты), только MCP-путь.

Тесты / приёмка

  • ($ticket_lifetime=2592000), foo_bar_baz, голый домен, backticks — через createPage/updatePageMarkdown сохраняются литерально; https://… остаётся ссылкой; ## заголовок и списки работают.
  • Импорт файла (не-MCP путь) с $x^2$ — формула создаётся (дефолты нетронуты).
  • Snapshot-тест стабильности text-вывода; сценарий «записал конфиг → прочитал text → дифф пустой».

Связанное

Фидбек-пакет: #501 (токен), #503 (hardBreak — text-режим зависит от его \n-семантики). Контракт DocmostClientLike, регистрация в обоих хостах, пересборка build/* обоих пакетов.

# Суть Две зеркальные боли агентского фидбека: markdown-слой при машинной работе искажает данные. **Запись**: `($ticket_lifetime=2592000)` превращается в LaTeX-формулу, `gitea.vvzvlad.xyz` авто-линкуется. **Чтение под дифф**: авто-ссылки и склейки строк дают ложные расхождения. По коду виновник записи — **не markdown как таковой** (CommonMark `foo_bar_baz` не курсивит), а два расширения поверх: кастомный `$...$`-регексп ([math-inline.ts](packages/prosemirror-markdown/src/lib/math-inline.ts)) канонического импортера и автолинк бессхемных доменов. «`_` → нижний индекс» — это KaTeX уже внутри ошибочно созданной math-ноды. **Решение по итогам обсуждения (зафиксировано владельцем):** - math и автолинк голых доменов в MCP-markdown-записи **отключаются совсем, без флагов и opt-in**. Кому нужна формула — `update_page_json` с `mathInline`/`mathBlock`-нодой: JSON-путь и существует для сложных случаев. - Первоначальный вариант «plain-режим в обход парсера целиком» **отклонён**: отбирает у агента структуру (заголовки/списки/фенсы), создаёт второй канон производства PM (проект осознанно держит один — см. «no second canon» в [markdown-fragment.ts](packages/mcp/src/lib/markdown-fragment.ts) и боль #293). # Решение ## 1. Запись: параметризовать канонический импортер, MCP зовёт с выключенными расширениями `markdownToProseMirror(md, { parseMath: false, fuzzyLinkify: false })`: - `parseMath: false` — `$...$`-регексп не применяется, доллары остаются текстом; - `fuzzyLinkify: false` — автолинк только явных `https://…`-URL; голые домены — текст; `[text](url)` работает как всегда. MCP-инструменты (`createPage`, `updatePageMarkdown`, markdown-фрагменты insert/patch) зовут импортер ТОЛЬКО так — без флагов в контракте инструментов. Другие потребители импортера (полный импорт файлов, git-sync) — дефолты не меняются (там markdown пишут люди/экспортёры, math легитимен). В описания write-инструментов добавить: «литеральные блоки (конфиги, shell) — оборачивай в код-фенсы; формулы — через update_page_json». ## 2. Чтение: `format: "text"` у getPage Плоский детерминированный текст узлов для машинного диффа: инлайн-марки/якоря/автоформат отброшены, блок = строка(и), hardBreak = `\n` (после #503). Реализация — переиспользовать серверный `jsonToText`-путь (питает tsvector), не изобретать второй сериализатор. Стабильность вывода между версиями пакета = контракт, закрепляется snapshot-тестом. # Крайние случаи - **Существующие агентские markdown-записи с `$...$`**: перестанут создавать формулы — это и есть цель (владелец: формулы через JSON). Человеческий редактор и импорт файлов не затронуты. - **Round-trip export→import** (`export_page_markdown`, lossless из #328): экспорт может содержать `$…$`-текст из старых формул? Нет — экспорт эмитит math-ноды своей разметкой, а ИМПОРТ полных файлов идёт с дефолтами (math on) — lossless-пара не рвётся. Зафиксировать тестом. - **Не-текстовые ноды в text-режиме**: детерминированные плейсхолдеры (`[image]`, `[table 3x4]`) — стабильно и задокументировано в описании инструмента. - **git-sync** использует тот же импортер — его вызовы НЕ трогаем (дефолты), только MCP-путь. # Тесты / приёмка - `($ticket_lifetime=2592000)`, `foo_bar_baz`, голый домен, backticks — через createPage/updatePageMarkdown сохраняются литерально; `https://…` остаётся ссылкой; `## заголовок` и списки работают. - Импорт файла (не-MCP путь) с `$x^2$` — формула создаётся (дефолты нетронуты). - Snapshot-тест стабильности text-вывода; сценарий «записал конфиг → прочитал text → дифф пустой». # Связанное Фидбек-пакет: #501 (токен), #503 (hardBreak — text-режим зависит от его `\n`-семантики). Контракт `DocmostClientLike`, регистрация в обоих хостах, пересборка build/* обоих пакетов.
agent_vscode changed title from [feat][mcp] Литеральные режимы для агентов: запись без markdown-интерпретации (plain) + чтение сырого текста для машинного диффа to [feat][mcp] Убрать math и автолинк голых доменов из MCP-markdown-записи (без флагов; формулы — через JSON) + text-режим чтения для машинного диффа 2026-07-11 05:22:23 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: vvzvlad/gitmost#502