docs(mcp): точные описания потерь getPage/exportPageMarkdown — конвертер канонический (#415)
Описания двух тулов устарели после #345 (канонический конвертер, #293/#351): «LOSSY…approximated» у getPage и «lossless» у exportPageMarkdown искажали роутинг агентов и молчаливо скрывали реальную потерю данных. Четвёртый линк breaking-окна, стоит на #413. - tool-specs.ts: getPage — вместо «LOSSY…approximated» точный закрытый список потерь (canonical for text; теряет лишь id блоков, resolved-якоря комментариев и фиксированный набор ACCEPTED-атрибутов без markdown- представления: спаны/colwidth/фон ячеек таблиц, indent, callout.icon, orderedList.type, link internal/target/rel/class). exportPageMarkdown — убрано «lossless»: round-trip перегенерирует id блоков и молча отбрасывает тот же набор (в первую очередь merge-спаны ячеек); держать в page-JSON, если нужны. Список выведен из источника истины (ATTR_VALUE_FUZZ_ALLOWLIST + MARK_ATTR_ALLOWLIST); opaque carried-verbatim токены (attachmentId, mime, slugId и пр.) намеренно НЕ в списке потерь — они round-trip'ятся. - server-instructions.ts: READ-строка ROUTING_PROSE приведена к тому же точному списку, без противоречия markdown-default роутингу #413. - Исправлена вторая латентная неточность: getPage описывался как сохраняющий resolved-якоря — но client.ts передаёт dropResolvedCommentAnchors: true, resolved-якоря скрыты (getNode, наоборот, их сохраняет — другой тул). - README пакета (EN + RU-зеркало) приведены в соответствие passage-for-passage: убраны безоговорочные «lossless/lossy» для Markdown round-trip; genuinely- lossless ссылки на raw-JSON (getPageJson/getNode) оставлены. Логику не трогает. server-instructions.test.mjs удалён и заменён tool-inventory.test.mjs (без пинов на старую формулировку). mcp tsc чисто, node --test 702/702, tool-inventory 5/5. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+18
-11
@@ -40,7 +40,7 @@ There are several Docmost MCPs. Here is a capability-by-capability comparison.
|
||||
| **Enterprise license required** | **No** | **Yes** | No | No | No |
|
||||
| Authentication | email + password, **auto re-auth** | API key | email + password | cookie `authToken` (copy from DevTools) | Docmost API / **direct PostgreSQL** |
|
||||
| Read page as Markdown | ✅ | ✅ | ✅ | ✅ | ✅ (read-only) |
|
||||
| **Lossless Markdown round-trip** (export / import, keeps comment anchors) | ✅ | — | — | — | — |
|
||||
| **Markdown round-trip** (export / import, keeps comment anchors) | ✅ | — | — | — | — |
|
||||
| Read **lossless ProseMirror JSON** (with block ids) | ✅ | — | — | — | — |
|
||||
| **Compact page outline** (cheap block-id lookup) | ✅ | — | — | — | — |
|
||||
| **Fetch a single block** (by id or index) | ✅ | — | — | — | — |
|
||||
@@ -115,8 +115,10 @@ All 41 tools, grouped by what you'd reach for them.
|
||||
- **`listPages`** — Recent pages in a space, ordered by `updatedAt` desc (default 50,
|
||||
max 100). Use `search` for lookups in large spaces.
|
||||
- **`search`** — Full-text search across pages and content (bounded by `limit`, max 100).
|
||||
- **`getPage`** — A page's content as clean **Markdown** (convenient, but a *lossy*
|
||||
view — block ids and exact table/callout structure are approximated).
|
||||
- **`getPage`** — A page's content as clean **Markdown** (canonical for text; drops only
|
||||
block ids, resolved-comment anchors, and a fixed no-Markdown-representation attr set —
|
||||
table spans/colwidth/background, indent, `callout.icon`, `orderedList.type`, and link
|
||||
`internal`/`target`/`rel`/`class`; use `getPageJson` when you need those).
|
||||
- **`getPageJson`** — A page's **lossless ProseMirror/TipTap JSON**, including every
|
||||
block's `attrs.id` and the `slugId` used in URLs. This is what the per-block editing
|
||||
tools consume.
|
||||
@@ -186,10 +188,14 @@ All 41 tools, grouped by what you'd reach for them.
|
||||
|
||||
### Markdown round-trip
|
||||
|
||||
- **`exportPageMarkdown`** — Export a page to a single self-contained, **lossless
|
||||
Docmost-flavoured Markdown** file: a meta header, the body with inline comment anchors
|
||||
and diagrams, and a trailing comments-thread block. To replace a page's body from plain
|
||||
authoring Markdown, use `updatePageMarkdown`.
|
||||
- **`exportPageMarkdown`** — Export a page to a single self-contained
|
||||
**Docmost-flavoured Markdown** file: a meta header, the body with inline comment anchors
|
||||
and diagrams, and a trailing comments-thread block. The download → edit → import
|
||||
round-trip regenerates block ids and **silently drops** the no-Markdown-representation
|
||||
attr set (table merge spans/colwidth/background, indent, `callout.icon`,
|
||||
`orderedList.type`, link `internal`/`target`/`rel`/`class`); keep those in ProseMirror
|
||||
JSON if they must survive. To replace a page's body from plain authoring Markdown, use
|
||||
`updatePageMarkdown`.
|
||||
|
||||
> **Removed in this release:** `importPageMarkdown` (the round-trip parser for an
|
||||
> exported Docmost-Markdown file) is **no longer exposed on the external MCP surface**.
|
||||
@@ -293,15 +299,16 @@ so capable clients steer the model automatically.
|
||||
refreshed automatically on the first 401/403 (covering JSON, multipart upload, and the
|
||||
collaboration-token path), with in-flight login de-duplication so a burst of calls
|
||||
triggers a single re-login.
|
||||
- **Lossless and lossy reads.** `getPageJson` returns the exact ProseMirror tree with
|
||||
block ids; `getPage` returns clean Markdown for convenience.
|
||||
- **Precise reads.** `getPageJson` returns the exact ProseMirror tree with block ids;
|
||||
`getPage` returns canonical Markdown that drops only a fixed, documented attr set.
|
||||
- **Full Docmost schema.** Markdown↔ProseMirror conversion supports callouts (including
|
||||
nested), task lists (bullet *and* numbered checklists), tables, math blocks, embeds,
|
||||
highlights, sub/superscript and more, with defensive caps against pathological input.
|
||||
- **Structured tables & lossless Markdown round-trip.** Tables can be edited as a matrix
|
||||
- **Structured tables & Markdown round-trip.** Tables can be edited as a matrix
|
||||
(read, insert/delete rows, set cells by `[row,col]`) without resending the document, and
|
||||
a page can be exported to and re-imported from a self-contained Docmost-flavoured
|
||||
Markdown file that preserves inline comment anchors and diagrams.
|
||||
Markdown file that preserves inline comment anchors and diagrams (block ids regenerate
|
||||
and a fixed no-Markdown-representation attr set is dropped — see `exportPageMarkdown`).
|
||||
- **Token-optimized responses.** API responses are filtered down to the fields agents
|
||||
actually need, and large collections (spaces, pages, comments, history) are paginated.
|
||||
- **Hardened runtime.** Global handlers keep a stray socket error from tearing down the
|
||||
|
||||
+21
-11
@@ -43,7 +43,7 @@ Docmost-MCP не сочетают:
|
||||
| **Нужна enterprise-лицензия** | **Нет** | **Да** | Нет | Нет | Нет |
|
||||
| Аутентификация | email + пароль, **авто-переавторизация** | API-ключ | email + пароль | cookie `authToken` (копировать из DevTools) | API Docmost / **напрямую PostgreSQL** |
|
||||
| Чтение страницы как Markdown | ✅ | ✅ | ✅ | ✅ | ✅ (только чтение) |
|
||||
| **Lossless Markdown round-trip** (экспорт/импорт, сохраняет якоря комментариев) | ✅ | — | — | — | — |
|
||||
| **Markdown round-trip** (экспорт/импорт, сохраняет якоря комментариев) | ✅ | — | — | — | — |
|
||||
| Чтение **lossless ProseMirror JSON** (с id блоков) | ✅ | — | — | — | — |
|
||||
| **Компактная структура страницы** (дешёвый поиск id блока) | ✅ | — | — | — | — |
|
||||
| **Получение одного блока** (по id или индексу) | ✅ | — | — | — | — |
|
||||
@@ -119,8 +119,11 @@ Docmost-MCP не сочетают:
|
||||
50, максимум 100). Для поиска в больших пространствах используйте `search`.
|
||||
- **`search`** — Полнотекстовый поиск по страницам и контенту (ограничен `limit`, максимум
|
||||
100).
|
||||
- **`getPage`** — Контент страницы как чистый **Markdown** (удобно, но это
|
||||
*lossy*-представление — id блоков и точная структура таблиц/коллаутов аппроксимируются).
|
||||
- **`getPage`** — Контент страницы как чистый **Markdown** (канонично для текста; теряет
|
||||
лишь id блоков, якоря разрешённых комментариев и фиксированный набор атрибутов без
|
||||
markdown-представления — спаны/colwidth/фон ячеек таблиц, отступы (indent),
|
||||
`callout.icon`, `orderedList.type` и `internal`/`target`/`rel`/`class` у ссылок;
|
||||
используйте `getPageJson`, когда они нужны).
|
||||
- **`getPageJson`** — **Lossless ProseMirror/TipTap JSON** страницы, включая `attrs.id`
|
||||
каждого блока и `slugId`, используемый в URL. Именно его потребляют инструменты
|
||||
поблочного редактирования.
|
||||
@@ -191,10 +194,14 @@ Docmost-MCP не сочетают:
|
||||
|
||||
### Markdown: экспорт и импорт
|
||||
|
||||
- **`exportPageMarkdown`** — Экспортировать страницу в один самодостаточный, **lossless
|
||||
Markdown в диалекте Docmost**: мета-заголовок, тело с inline-якорями комментариев и
|
||||
диаграммами и завершающий блок тредов комментариев. Чтобы заменить тело страницы из
|
||||
обычного авторского Markdown, используйте `updatePageMarkdown`.
|
||||
- **`exportPageMarkdown`** — Экспортировать страницу в один самодостаточный
|
||||
**Markdown в диалекте Docmost**: мета-заголовок, тело с inline-якорями комментариев и
|
||||
диаграммами и завершающий блок тредов комментариев. Round-trip скачать → отредактировать →
|
||||
импортировать перегенерирует id блоков и **молча отбрасывает** набор атрибутов без
|
||||
markdown-представления (спаны/colwidth/фон ячеек таблиц, отступы (indent), `callout.icon`,
|
||||
`orderedList.type`, `internal`/`target`/`rel`/`class` у ссылок); держите их в ProseMirror
|
||||
JSON, если они должны выжить. Чтобы заменить тело страницы из обычного авторского Markdown,
|
||||
используйте `updatePageMarkdown`.
|
||||
|
||||
> **Удалено в этом релизе:** `importPageMarkdown` (парсер round-trip для
|
||||
> экспортированного Docmost-Markdown-файла) **больше не отдаётся на внешней MCP-поверхности**.
|
||||
@@ -302,16 +309,19 @@ Docmost-MCP не сочетают:
|
||||
автоматически на первом 401/403 (покрывая JSON, multipart-загрузку и путь токена
|
||||
коллаборации), с дедупликацией параллельных логинов, так что пачка вызовов вызывает один
|
||||
повторный логин.
|
||||
- **Lossless- и lossy-чтение.** `getPageJson` возвращает точное дерево ProseMirror с id
|
||||
блоков; `getPage` возвращает чистый Markdown для удобства.
|
||||
- **Точные чтения.** `getPageJson` возвращает точное дерево ProseMirror с id блоков;
|
||||
`getPage` возвращает канонический Markdown, теряющий лишь фиксированный, документированный
|
||||
набор атрибутов.
|
||||
- **Полная схема Docmost.** Конвертация Markdown↔ProseMirror поддерживает коллауты
|
||||
(включая вложенные), списки задач (маркированные *и* нумерованные чек-листы), таблицы,
|
||||
блоки формул, эмбеды, выделение, под/надстрочный текст и прочее, с защитными лимитами
|
||||
против патологического ввода.
|
||||
- **Структурные таблицы и lossless Markdown round-trip.** Таблицы можно редактировать как
|
||||
- **Структурные таблицы и Markdown round-trip.** Таблицы можно редактировать как
|
||||
матрицу (чтение, вставка/удаление строк, задание ячеек по `[row, col]`) без пересылки
|
||||
документа, а страницу — экспортировать и заново импортировать как самодостаточный
|
||||
Markdown-файл в диалекте Docmost, сохраняющий inline-якоря комментариев и диаграммы.
|
||||
Markdown-файл в диалекте Docmost, сохраняющий inline-якоря комментариев и диаграммы
|
||||
(id блоков перегенерируются, а фиксированный набор атрибутов без markdown-представления
|
||||
отбрасывается — см. `exportPageMarkdown`).
|
||||
- **Ответы, оптимизированные по токенам.** Ответы API урезаются до полей, действительно
|
||||
нужных агентам, а большие коллекции (пространства, страницы, комментарии, история)
|
||||
пагинируются.
|
||||
|
||||
@@ -40,7 +40,7 @@ import { SHARED_TOOL_SPECS, SharedToolSpec } from "./tool-specs.js";
|
||||
*/
|
||||
export const ROUTING_PROSE =
|
||||
"Docmost editing guide — choose the tool by intent. The <tool_inventory> at the end lists every tool with a one-line purpose; the notes below are the routing hints for WHEN to reach for each.\n" +
|
||||
"READ: find a page -> search (workspace-wide full-text); list -> listPages / listSpaces. Locate blocks and their ids CHEAPLY -> getOutline (compact top-level map; start here, not getPageJson). One block, for editing -> getNode (by attrs.id, or \"#<index>\" for tables, which carry no id) — returns MARKDOWN by default (comment anchors kept for safe write-back); pass format:\"json\" for the raw ProseMirror subtree. Find every occurrence of a string/regex ON a page (and where each is) -> searchInPage, NOT block-by-block getNode — it returns each hit's node ref + block index + context for a targeted comment. Whole page -> getPage (Markdown, lossy; inline <span data-comment-id> tags are comment anchors — markup, not text) or getPageJson (lossless ProseMirror with block ids). Hand a huge page (with images) to an external consumer without pulling it through the model context -> stashPage (returns a short-lived anonymous URL).\n" +
|
||||
"READ: find a page -> search (workspace-wide full-text); list -> listPages / listSpaces. Locate blocks and their ids CHEAPLY -> getOutline (compact top-level map; start here, not getPageJson). One block, for editing -> getNode (by attrs.id, or \"#<index>\" for tables, which carry no id) — returns MARKDOWN by default (comment anchors kept for safe write-back); pass format:\"json\" for the raw ProseMirror subtree. Find every occurrence of a string/regex ON a page (and where each is) -> searchInPage, NOT block-by-block getNode — it returns each hit's node ref + block index + context for a targeted comment. Whole page -> getPage (Markdown, canonical for text; drops only block ids, resolved-comment anchors, and a fixed no-md-representation attr set: table spans/colwidth/bg, indent, callout.icon, orderedList.type, link internal/target/rel/class; inline <span data-comment-id> tags are comment anchors — markup, not text) or getPageJson (full ProseMirror with block ids, for those dropped attrs). Hand a huge page (with images) to an external consumer without pulling it through the model context -> stashPage (returns a short-lived anonymous URL).\n" +
|
||||
"EDIT: fix wording/typos/numbers -> editPageText (find/replace inside blocks, no node id needed). Edit a block -> getNode(markdown) -> edit the markdown -> patchNode(markdown) (by attrs.id from getOutline; the markdown fragment may be several blocks — a 1->N section rewrite in one call, the first block keeps the id). Reach for patchNode's `node`-JSON only for fine attr/mark work; a table cell with spans/colors/fixed width -> the table tools (patchNode markdown refuses it). Add a block -> insertNode (markdown, before/after a block by attrs.id or by anchor text, or append; `node` for raw JSON or bare table structure). Remove a block -> deleteNode (by attrs.id). Tables -> tableGet / tableUpdateCell / tableInsertRow / tableDeleteRow (address by \"#<index>\" from getOutline; table nodes have no attrs.id). Images -> insertImage (add from a web URL) / replaceImage (swap an existing image). Draw.io diagrams -> drawioCreate (create from mxGraph XML and insert), drawioGet (read a diagram as mxGraph XML + a hash), drawioUpdate (replace a diagram; pass the hash from drawioGet as baseHash for optimistic locking); before authoring a diagram, drawioShapes (look up verified stencil style-strings so a shape name never renders as an empty box) and drawioGuide (on-demand authoring reference: skeleton/layout/containers/icons-aws/icons-azure), and pass layout:\"elk\" to drawioCreate/drawioUpdate to auto-place nodes. Footnotes -> insertFootnote. Bulk/structural rewrite -> updatePageJson (full ProseMirror replace) or updatePageMarkdown (full plain-Markdown body replace, re-imported — block ids regenerate); prefer the granular tools above to avoid resending the whole ~100KB+ document. Complex/scripted rewrite (multiple coordinated edits, renumbering) -> docmostTransform: write a JS `(doc, ctx) => doc` transform, preview the diff with dryRun (default), then apply with dryRun:false; ctx.helpers includes commentsToFootnotes for turning inline comments into numbered footnotes.\n" +
|
||||
"PAGES: new -> createPage (Markdown). Rename (title only) -> renamePage. Move -> movePage. Delete -> deletePage (SOFT delete — the page goes to trash and is restorable; nothing is permanent). Copy/replace a page's whole content from another page (server-side, no document through the model) -> copyPageContent. Sharing -> sharePage / unsharePage / listShares; sharePage makes the page PUBLICLY accessible — do it only when explicitly asked.\n" +
|
||||
"COMMENTS: createComment is always inline and requires an EXACT selection — contiguous text from a single block, <=250 chars (fails rather than leaving an unanchored comment); reply to a thread via parentCommentId. Propose a concrete text fix for one-click human approval -> createComment with suggestedText (the exact plain-text replacement for the selection; the selection must then be UNIQUE in the page — extend it with context if needed); prefer this over editing directly when the change is subjective or needs the author's sign-off. Manage -> listComments, updateComment, resolveComment (resolve/reopen, reversible — prefer over delete to close), deleteComment, checkNewComments.\n" +
|
||||
|
||||
@@ -877,11 +877,17 @@ export const SHARED_TOOL_SPECS = {
|
||||
inAppKey: 'getPage',
|
||||
description:
|
||||
'Fetch a single page as Markdown by its id. Returns the page title and ' +
|
||||
'its Markdown content. The Markdown conversion is LOSSY (block ids, exact ' +
|
||||
'table/callout structure are approximated); for a lossless representation ' +
|
||||
'use the lossless page-JSON read tool. Inline <span data-comment-id> tags in the markdown ' +
|
||||
'are comment highlight anchors (also present for RESOLVED threads) — ' +
|
||||
'treat them as markup, not page text.',
|
||||
'its Markdown content. The converter is canonical (round-trips text and ' +
|
||||
'block structure), so this is sufficient for text edits; use the ' +
|
||||
'page-JSON read tool only when you need what Markdown cannot carry. The ' +
|
||||
'Markdown drops exactly: (1) block ids (not visible in Markdown); ' +
|
||||
'(2) resolved-comment anchors (hidden here; only active <span ' +
|
||||
'data-comment-id> anchors remain); (3) a fixed set of attributes with no ' +
|
||||
'Markdown representation — table-cell colspan/rowspan/colwidth/' +
|
||||
'backgroundColor/backgroundColorName, heading/paragraph indent, ' +
|
||||
'callout.icon, orderedList.type, and link internal/target/rel/class. ' +
|
||||
'Inline <span data-comment-id> tags in the markdown are comment highlight ' +
|
||||
'anchors — treat them as markup, not page text.',
|
||||
tier: 'core',
|
||||
catalogLine: 'getPage — fetch a page as Markdown by its id.',
|
||||
// Reconciled: MCP's stricter .min(1) kept; in-app's more-informative
|
||||
@@ -1240,13 +1246,17 @@ export const SHARED_TOOL_SPECS = {
|
||||
inAppKey: 'exportPageMarkdown',
|
||||
// CANONICAL: the MCP copy (a strict superset of the terse in-app wording).
|
||||
description:
|
||||
'Export a page to a single self-contained, lossless Docmost-flavoured ' +
|
||||
'Markdown file (custom extensions): YAML-free meta header, body with ' +
|
||||
'inline comment anchors and diagrams, and a trailing comments-thread ' +
|
||||
'block. Designed for a download -> edit body -> page-Markdown import ' +
|
||||
'round-trip that preserves everything, including comment highlights. ' +
|
||||
'Comment THREADS are preserved in the file but are not re-pushed to the ' +
|
||||
'server on import.',
|
||||
'Export a page to a single self-contained Docmost-flavoured Markdown ' +
|
||||
'file (custom extensions): YAML-free meta header, body with inline ' +
|
||||
'comment anchors (resolved ones kept) and diagrams, and a trailing ' +
|
||||
'comments-thread block. Designed for a download -> edit body -> ' +
|
||||
'page-Markdown import round-trip; block ids regenerate and comment ' +
|
||||
'THREADS, though kept in the file, are not re-pushed to the server on ' +
|
||||
'import. The round-trip SILENTLY DROPS a fixed set of attributes with no ' +
|
||||
'Markdown representation — table-cell merge spans (colspan/rowspan), ' +
|
||||
'colwidth, backgroundColor/backgroundColorName, heading/paragraph indent, ' +
|
||||
'callout.icon, orderedList.type, and link internal/target/rel/class. Use ' +
|
||||
'the page-JSON tools if those must survive.',
|
||||
tier: 'deferred',
|
||||
catalogLine:
|
||||
'exportPageMarkdown — export a page to self-contained Markdown (body + comments).',
|
||||
|
||||
Reference in New Issue
Block a user