# Docmost MCP Server [English](README.md) · **Русский** Сервер Model Context Protocol (MCP) для [Docmost](https://docmost.com/), который позволяет ИИ-агентам **читать, искать, писать, реструктурировать, рецензировать, вести версии, комментировать, иллюстрировать и публиковать** документацию — безопасно, на живом инстансе и без enterprise-лицензии. > **Написан агентом для агентов.** Человек правит документ глазами и руками: читает, > заходит в редактор, перепечатывает. Агент работает иначе — ему гораздо проще *написать > небольшую функцию, которая чинит текст*, чем перечитывать и заново выдавать весь > документ. Поэтому сервер построен вокруг того, как модели на самом деле удобно > редактировать: адресовать блок по id, сделать find/replace или передать трансформ > `(doc, ctx) => doc` и позволить модели *запрограммировать* правку. `docmost_transform` — > это и есть такой интерфейс. Другие Docmost-MCP «заточены под человека» — они дают > «открыть страницу» и «заменить страницу»; этот даёт примитивы редактирования, в которых > модель сильна. Сервер предоставляет **38 инструментов**, построенных вокруг трёх идей, которые другие Docmost-MCP не сочетают: 1. **Точечные, экономичные по токенам правки.** Адресуйте отдельный блок по id и патчите его или делайте find/replace вместо того, чтобы гонять весь документ ~100 КБ через модель. 2. **Безопасная запись на живой документ.** Каждая мутация проходит через слой коллаборации реального времени (тот же WebSocket, что использует веб-редактор), сериализуется по странице, поэтому никогда не затирает параллельную правку человека и подтверждается как сохранённая до возврата из инструмента. 3. **Настоящая страховка.** История версий, дифф, эквивалентный Docmost, восстановление одним вызовом и предпросмотр (dry-run) для скриптовых правок — чтобы агент мог редактировать смело, а вы всегда могли увидеть и откатить сделанное. --- ## Почему именно этот сервер (в сравнении с альтернативами) Существует несколько Docmost-MCP. Ниже — сравнение по возможностям. «Официальный» — встроенный MCP Docmost; остальные — community-проекты на GitHub. | Возможность | **Этот сервер** | Официальный (встроенный) | MrMartiniMo/docmost-mcp | cyborgx0x/mcp-docmost | aleksvin8888 / isak-landin | | --- | :---: | :---: | :---: | :---: | :---: | | **Нужна enterprise-лицензия** | **Нет** | **Да** | Нет | Нет | Нет | | Аутентификация | email + пароль, **авто-переавторизация** | API-ключ | email + пароль | cookie `authToken` (копировать из DevTools) | API Docmost / **напрямую PostgreSQL** | | Чтение страницы как Markdown | ✅ | ✅ | ✅ | ✅ | ✅ (только чтение) | | **Lossless Markdown round-trip** (экспорт/импорт, сохраняет якоря комментариев) | ✅ | — | — | — | — | | Чтение **lossless ProseMirror JSON** (с id блоков) | ✅ | — | — | — | — | | **Компактная структура страницы** (дешёвый поиск id блока) | ✅ | — | — | — | — | | **Получение одного блока** (по id или индексу) | ✅ | — | — | — | — | | Создание / перемещение / удаление страниц | ✅ | ✅ | ✅ | ✅ | — | | **Поблочные правки** (patch/insert/delete по id) | ✅ | — | — | — | — | | **Хирургический find/replace** (с сохранением структуры) | ✅ | — | — | — | — | | **Скриптовый JS-трансформ** (песочница, dry-run дифф) | ✅ | — | — | — | — | | **Структурное редактирование таблиц** (CRUD строк/ячеек) | ✅ | — | — | — | — | | **История версий** страницы | ✅ | — | — | ✅ | — | | **Дифф двух версий** | ✅ | — | — | — | — | | **Восстановление версии** (обратимое) | ✅ | — | — | — | — | | **Комментарии** (CRUD + inline-привязка) | ✅ | — | — | ✅ | — | | **Поллинг новых комментариев** с момента времени | ✅ | — | — | — | — | | **Изображения** (вставка / замена) | ✅ | — | — | — | — | | **Публичные ссылки** (создать / отозвать / список) | ✅ | — | — | — | — | | Экспорт в HTML / PDF | — | — | — | ✅ | — | | **Безопасная запись через real-time-collab** (без затирания, с подтверждением) | ✅ | n/a | ✅ | — | n/a (только чтение) | ### Что это даёт на практике - **Никакого enterprise-налога.** Официальный MCP Docmost — enterprise-функция: нужна активная enterprise-лицензия. Этот сервер — MIT и работает с *любым* self-hosted Docmost через стандартный API + сокет коллаборации, имея лишь email и пароль аккаунта. - **Экономия токенов при редактировании.** Большинство Docmost-MCP (и официальный) предлагают только запись «заменить всю страницу» — агент вынужден скачать весь документ, изменить и загрузить обратно, оплачивая весь документ **дважды** на каждой мелкой правке. Этот сервер позволяет агенту изменить ровно один блок (`patch_node` / `insert_node` / `delete_node`), сделать find/replace с сохранением структуры (`edit_page_text`) или скопировать страницу на стороне сервера (`copy_page_content`) — **причём документ ни разу не проходит через модель**. - **Записи, которые не воюют с редактором.** Наивная запись через REST конфликтует с тем, что в этот момент печатает человек, и может молча затереть его правки или упасть на дебаунс-сохранении Docmost. Этот сервер применяет каждое изменение через живой документ коллаборации (Hocuspocus/Yjs), читая и записывая **синхронно в пределах одного тика синхронизации**, чтобы никакая параллельная правка не вклинилась, сериализует записи **по странице** мьютексом и **ждёт подтверждения сохранения от сервера** до возврата. Если сокет отвалился посреди записи, инструмент возвращает ошибку, а не ложный успех. - **Агентоориентированная модель редактирования.** Серверы «под человека» дают «открыть страницу» и «заменить страницу», потому что это отражает то, как работает человек. Модель редактирует лучше, *программируя* правку — адресуя блоки по id, делая find/replace или передавая трансформ `(doc, ctx) => doc` (`docmost_transform`, с dry-run диффом перед коммитом). Этот сервер построен вокруг этого — поэтому у него есть примитивы редактирования, которых у остальных просто нет. - **Страховка при редактировании, которой нет у других.** `list_page_history` → `diff_page_versions` → `restore_page_version` дают агенту (и вам) полный цикл «посмотреть и откатить». Дифф использует *тот же* конвейер `recreateTransform → ChangeSet → simplifyChanges`, что и встроенный просмотр истории Docmost, так что результат совпадает с продуктом. - **Удобство вместо выковыривания cookie.** Некоторые community-серверы аутентифицируются, заставляя вас копировать сессионный cookie из DevTools браузера (он истекает), либо лезут **напрямую в базу PostgreSQL**. Этот сервер логинится по учётным данным и **прозрачно переавторизуется на 401/403** (с дедупликацией параллельных логинов), поэтому долгоживущие агенты не падают, когда токен истёк. Он также соблюдает контроль доступа Docmost, потому что ходит через API и сервер коллаборации как обычный пользователь. --- ## Инструменты Все 38 инструментов, сгруппированы по задачам, для которых вы их возьмёте. ### Чтение и поиск - **`get_workspace`** — Информация о текущем воркспейсе Docmost. - **`list_spaces`** — Все пространства воркспейса. - **`list_pages`** — Недавние страницы пространства, по убыванию `updatedAt` (по умолчанию 50, максимум 100). Для поиска в больших пространствах используйте `search`. - **`search`** — Полнотекстовый поиск по страницам и контенту (ограничен `limit`, максимум 100). - **`get_page`** — Контент страницы как чистый **Markdown** (удобно, но это *lossy*-представление — id блоков и точная структура таблиц/коллаутов аппроксимируются). - **`get_page_json`** — **Lossless ProseMirror/TipTap JSON** страницы, включая `attrs.id` каждого блока и `slugId`, используемый в URL. Именно его потребляют инструменты поблочного редактирования. - **`get_outline`** — Компактная структура страницы из блоков верхнего уровня (`{index, type, id, level, firstText}`; для таблиц добавляются число строк/столбцов и тексты ячеек заголовка, для списков — число пунктов) **без** тела документа. Дешёвый способ найти раздел или таблицу и получить id блока перед `get_node` / `patch_node` / `insert_node`. - **`get_node`** — Получить полное ProseMirror-поддерево одного блока (lossless), не вытягивая всю страницу. Адресуйте его по id блока (из `get_outline` / `get_page_json`) или формой `#` для блока верхнего уровня — используйте `#` для таблиц/строк/ячеек, у которых нет id. ### Жизненный цикл страниц - **`create_page`** — Создать страницу из Markdown и поместить в иерархию (опционально `parentPageId`) одним вызовом. Использует import API Docmost для чистой конвертации Markdown→ProseMirror. - **`rename_page`** — Изменить только заголовок страницы, не трогая и не пересылая контент. - **`move_page`** — Сменить родителя страницы (вложить или вынести в корень); поддерживает позиционирование по fractional-index. Возвращает успех только при *положительно подтверждённом* результате. - **`delete_page`** — Удалить одну страницу. - **`copy_page_content`** — Заменить тело одной страницы копией тела другой, **полностью на стороне сервера** — документ не проходит через модель. У целевой страницы сохраняются собственные заголовок и slug (URL не меняется). ### Редактирование - **`edit_page_text`** — Хирургический find/replace внутри текста страницы. Сохраняет **всю** структуру: id блоков, marks, ссылки, коллауты, таблицы. Предпочтительный инструмент для правки формулировок, опечаток, чисел и имён. - **`patch_node`** — Заменить один блок, адресованный по `attrs.id` (из `get_page_json`), без пересылки документа. - **`insert_node`** — Вставить блок до/после другого (по `attrs.id` или по якорному тексту) либо добавить в конец. - **`delete_node`** — Удалить один блок по его `attrs.id`. - **`update_page_json`** — Заменить весь контент страницы документом ProseMirror (массовые перезаписи или когда у узлов нет id). `content` опционален — опустите его, чтобы изменить только заголовок. Сохраняет переданные id блоков, поэтому якоря заголовков и история остаются стабильными. - **`docmost_transform`** — Агентоориентированный интерфейс редактирования: вместо перепечатывания документа агент **пишет функцию, которая его чинит**. Редактирует страницу, запуская произвольный **JS-трансформ `(doc, ctx) => doc`** на её *живом* документе ProseMirror. Работает в **песочнице** (без `require`/`process`/`fs`/сети, таймаут 5 с). **По умолчанию dry-run**: возвращает предпросмотр диффа без записи; установите `dryRun:false`, чтобы применить атомарно. `ctx` даёт доступ к комментариям страницы и набору хелперов (`walk`, `getList`, `blockText`, `insertMarkerAfter`, `setCalloutRange`, `commentsToFootnotes`, …) для многошаговых согласованных перезаписей — например перенумерации или превращения inline-комментариев в нумерованные сноски. ### Таблицы - **`table_get`** — Прочитать таблицу как матрицу: `{rows, cols, cells (text[][]), cellIds}` (id абзаца на ячейку или `null`). Адресуйте таблицу через `#` (из `get_outline`) или любой id блока внутри неё. Используйте `cellIds` вместе с `patch_node` для правок ячеек с форматированием. - **`table_insert_row`** — Вставить строку из текстовых ячеек, дополненную до числа столбцов таблицы (передать ячеек больше числа столбцов — ошибка). `index` — 0-based позиция вставки (0 вставляет перед заголовком); опустите, чтобы добавить в конец. - **`table_delete_row`** — Удалить строку по 0-based `index`. Отказывается удалять единственную строку таблицы; удаление строки 0 делает заголовком следующую строку. - **`table_update_cell`** — Задать текстовое содержимое ячейки `[row, col]` (0-based). Для форматирования используйте `patch_node` по id абзаца ячейки из `table_get`. ### Markdown: экспорт и импорт - **`export_page_markdown`** — Экспортировать страницу в один самодостаточный, **lossless Markdown в диалекте Docmost**: мета-заголовок, тело с inline-якорями комментариев и диаграммами и завершающий блок тредов комментариев. Рассчитан на цикл «скачать → отредактировать тело → `import_page_markdown`», сохраняющий всё, включая выделения комментариев. - **`import_page_markdown`** — Заменить контент страницы из Markdown-файла в диалекте Docmost, созданного `export_page_markdown`, восстанавливая якоря-выделения комментариев и диаграммы из их inline-HTML. (Треды комментариев из файла не пересоздаются на сервере — записываются только тело страницы и inline-марки комментариев; тредами управляйте через инструменты/UI комментариев.) ### Изображения - **`insert_image`** — Загрузить локальное изображение и вставить за один шаг: добавить в конец, поставить вместо текстового плейсхолдера (`replaceText`) или после заданного блока (`afterText`). Сохраняет id всех остальных блоков. - **`replace_image`** — Заменить существующее изображение. Загружает новый файл как **новое вложение** (чистый URL, который рендерится и сбрасывает кэш браузера), затем перенаправляет все узлы, ссылавшиеся на старое вложение (рекурсивно, включая коллауты/таблицы), через живой документ, сохраняя комментарии, выравнивание и alt-текст. (Перезапись «по месту» намеренно не используется — некоторые версии Docmost портят вложение при перезаписи.) ### Комментарии - **`create_comment`** — Добавить комментарий к странице, опционально **привязав inline** к точному фрагменту текста (первое вхождение оборачивается comment-маркой). - **`list_comments`** — Список комментариев страницы (контент возвращается как Markdown). - **`update_comment`** — Изменить существующий комментарий. - **`delete_comment`** — Удалить комментарий. - **`check_new_comments`** — Найти комментарии, созданные после заданной метки времени ISO-8601, по пространству, опционально в рамках поддерева страниц — идеально для агента, который следит за обратной связью в документе. ### Версии и история - **`list_page_history`** — Сохранённые версии страницы (Docmost авто-снапшотит при каждом сохранении), новые сверху, курсорная пагинация. id каждого элемента — это `historyId`. - **`diff_page_versions`** — Дифф двух версий (или версии против живой страницы). Возвращает вставленный/удалённый текст, счётчики целостности (изображения, ссылки, таблицы, коллауты, маркеры сносок) и человекочитаемую Markdown-сводку — посчитано тем же конвейером, что использует встроенный просмотр истории Docmost. - **`restore_page_version`** — Записать сохранённую версию обратно как текущий контент. У Docmost нет эндпоинта восстановления, поэтому создаётся **новый** снапшот — само восстановление тоже обратимо. ### Публикация - **`share_page`** — Сделать страницу публично доступной (идемпотентно) и вернуть её публичный URL (`/share//p/`); опционально индексирование поисковиками. - **`unshare_page`** — Отозвать публичный доступ к странице. - **`list_shares`** — Все публичные ссылки воркспейса с заголовками и публичными URL. --- ## Как выбрать инструмент редактирования Та же подсказка отдаётся в рантайме через поле `instructions` MCP-сервера, так что подходящие клиенты направляют модель автоматически. - **Правки текста** (формулировки, опечатки, числа): `edit_page_text`. - **Один блок** (абзац/заголовок/коллаут/ячейка таблицы): `patch_node` / `insert_node` / `delete_node`, адресуя узел по его `attrs.id` из `get_page_json`. - **Изображения**: `insert_image` / `replace_image`. - **Новая страница**: `create_page`. - **Массовая перезапись или узлы без id**: `update_page_json`. - **Многошаговая / скриптовая перезапись** (перенумерация, сноски, согласованные правки): `docmost_transform` — предпросмотр через `dryRun`, затем применение. - **Скопировать контент целой страницы из другой** (на стороне сервера): `copy_page_content`. - **Переименовать страницу** (только заголовок): `rename_page`. - **Чтение**: `get_page` (Markdown) / `get_page_json` (lossless ProseMirror с id). - **Просмотр изменений**: `list_page_history` → `diff_page_versions` → `restore_page_version`. - **Комментарии**: `create_comment` (с опциональной inline-привязкой) / `list_comments` / `update_comment` / `delete_comment` / `check_new_comments`. - **Дешёвая навигация по странице** (найти раздел/таблицу, получить id блока): `get_outline` → `get_node`. - **Таблицы** (добавить/удалить строку, задать ячейку): `table_get` / `table_insert_row` / `table_delete_row` / `table_update_cell`. - **Round-trip страницы через Markdown** (скачать, отредактировать, залить обратно без потерь, с комментариями): `export_page_markdown` / `import_page_markdown`. --- ## Как это устроено (технические детали) - **Безопасная запись через коллаборацию реального времени.** Мутации контента применяются через WebSocket коллаборации Docmost (Hocuspocus + Yjs). Сервер подключается, ждёт первичной синхронизации, чтобы локальный документ отражал авторитетный серверный (включая правки, которых ещё нет в дебаунс-снапшоте REST), затем **читает → трансформирует → пишет синхронно** в одном тике, чтобы никакое удалённое обновление не вклинилось, и **ждёт подтверждения сохранения** до возврата. - **Сериализация записи по странице.** Асинхронный мьютекс по `pageId` гарантирует, что две записи MCP в одну страницу никогда не пересекаются; разные страницы друг друга не блокируют. - **Прозрачная переавторизация.** Логин по email/паролю; истёкшие токены обновляются автоматически на первом 401/403 (покрывая JSON, multipart-загрузку и путь токена коллаборации), с дедупликацией параллельных логинов, так что пачка вызовов вызывает один повторный логин. - **Lossless- и lossy-чтение.** `get_page_json` возвращает точное дерево ProseMirror с id блоков; `get_page` возвращает чистый Markdown для удобства. - **Полная схема Docmost.** Конвертация Markdown↔ProseMirror поддерживает коллауты (включая вложенные), списки задач (маркированные *и* нумерованные чек-листы), таблицы, блоки формул, эмбеды, выделение, под/надстрочный текст и прочее, с защитными лимитами против патологического ввода. - **Структурные таблицы и lossless Markdown round-trip.** Таблицы можно редактировать как матрицу (чтение, вставка/удаление строк, задание ячеек по `[row, col]`) без пересылки документа, а страницу — экспортировать и заново импортировать как самодостаточный Markdown-файл в диалекте Docmost, сохраняющий inline-якоря комментариев и диаграммы. - **Ответы, оптимизированные по токенам.** Ответы API урезаются до полей, действительно нужных агентам, а большие коллекции (пространства, страницы, комментарии, история) пагинируются. - **Закалённый рантайм.** Глобальные обработчики не дают случайной ошибке сокета уронить stdio-сервер; `move_page` требует положительно подтверждённого успеха; движок диффа откатывается к грубому поблочному диффу, а не падает на патологическом документе. --- ## Установка ```bash npm install npm run build ``` ## Конфигурация Серверу нужны три переменные окружения: - `DOCMOST_API_URL` — полный URL к API вашего Docmost (например, `https://docs.example.com/api`). - `DOCMOST_EMAIL` — email аккаунта для аутентификации. - `DOCMOST_PASSWORD` — пароль аккаунта. ## Использование с Claude Desktop / произвольным MCP-клиентом Добавьте сервер в конфигурацию MCP (например, `claude_desktop_config.json`): ```json { "mcpServers": { "docmost-local": { "command": "node", "args": ["./build/index.js"], "env": { "DOCMOST_API_URL": "http://localhost:3000/api", "DOCMOST_EMAIL": "test@docmost.com", "DOCMOST_PASSWORD": "test" } } } } ``` ## Разработка ```bash # Режим наблюдения npm run watch # Сборка npm run build # Тесты (unit + mock; live end-to-end набор требует запущенного Docmost) npm test npm run test:e2e ``` ## Происхождение и благодарности Проект начинался как форк [MrMartiniMo/docmost-mcp](https://github.com/MrMartiniMo/docmost-mcp) (автор Moritz Krause) и существенно его расширяет — добавлены поблочное редактирование узлов, хирургические правки текста, песочница `docmost_transform`, история версий / дифф / восстановление, комментарии, вставка/замена изображений, публичные ссылки, серверное копирование страниц, двойное чтение JSON/Markdown, прозрачная переавторизация и значительное упрочнение. Инструменты комментариев портированы из upstream PR #3 от Max Nikitin. Спасибо обоим. ## Лицензия MIT