Skip to main content
Glama

Doco

📖 中文版

The document space where humans and AI agents write together. An open-source rich-text collaborative editor that puts your data back in your hands — and treats your AI agents with the same care: block-level stable addressing, optimistic concurrency control, and a 29-tool MCP server, so agents read and write your knowledge base as safely as a careful human editor.

  • Hosted: doco.page — free during beta

  • Connect your agent: claude mcp add doco -- npx -y --package doco-agent-cli doco mcp

  • CLI: npm i -g doco-agent-cli && doco login

  • npm: doco-agent-cli · API docs: doco.page/api-docs

Claude Code Plugin Marketplace

/plugin marketplace add songofhawk/doco
/plugin install doco@doco

The marketplace bundles the Doco MCP server and the safe read → version → protected-write operating protocol. Tokens remain in Claude Code's local configuration and are never included in the plugin repository.

Doco editor showing a live block-level Agent update in an English demo document

Why agents are safe here

Capability

What it means

Block-level stable addressing

Every paragraph has a block_<ULID> id — position-independent, survives drags and folds

Optimistic concurrency

Reads return a sha256 version; writes require If-Match; on 409 the agent re-reads, merges, retries — blind overwrites are impossible

Markdown round-trip

Export with ?annotate=anchors; write the whole document back and block ids are preserved

Human–agent co-editing

Agent writes flow through the same Yjs document — changes appear live in the browser

Transactions & idempotency

Batch operations commit atomically; Idempotency-Key makes retries side-effect-free

Related MCP server: session-coord-mcp

Features

Editing Experience

  • Rich text editing: headings, lists, blockquotes, task lists, code blocks (syntax highlighting), tables, images, links, text styling, and more

  • / slash command: type / to open the command palette with fuzzy search — supports pinyin abbreviations for Chinese users

  • Floating toolbar: auto-appears on text selection, all formatting actions within two centimeters of your cursor

  • Block drag-and-drop: hover the left edge of any paragraph to reveal a drag handle — reorder content like building blocks

  • Collapsible sections: fold away sections you're not working on; collapse state persists across sessions

  • Auto heading numbering: one-click toggle — H1–H4 headings automatically maintain hierarchical numbering (1. 1.1 1.1.1)

  • Keyboard shortcuts: ⌥↑/↓ move blocks, ⌘D duplicate blocks, ⌘⌥1/2/3/0 switch heading levels

Text-to-Diagram

Write Mermaid or PlantUML source code directly in your document. Diagrams render in place. Double-click to edit, fullscreen view, pinch-to-zoom — no more export-import-replace cycles with draw.io.

  • Mermaid: flowcharts, sequence diagrams, class diagrams, Gantt charts, state diagrams, and more

  • PlantUML: sequence diagrams, class diagrams, use case diagrams, component diagrams, and more

Spreadsheet

A full spreadsheet engine embedded in your documents:

  • Formula evaluation, cell formatting

  • Freeze panes, sort & filter

  • Cell merge / split

  • CSV import / export

Use it inline as a content block, or pop it out as a standalone full-screen spreadsheet.

Knowledge Base

  • Knowledge Base → Folders (nestable) → Documents — a three-level structure

  • Drag-and-drop reordering, renaming, and moving in the sidebar

  • Whole-KB ZIP export preserving folder hierarchy, with bundled images

  • Lossless native .doco.zip transfer for a document, folder, or whole knowledge base

Real-time Collaboration

Built on the Yjs CRDT algorithm:

  • No save button — changes sync automatically

  • Offline-first: browser IndexedDB is the primary store; the server holds a snapshot. Edit without a network, merge automatically when reconnected

  • Seamless device switching: close your laptop, pick up your phone, keep writing

Import / Export

Format

Import

Export

Doco native package

✅ Document / folder / KB

✅ Lossless document / folder / KB

Markdown

✅ Paste / file upload

✅ Single doc & KB bundle

Word (DOCX)

✅

✅

PDF

✅

✅

HTML

✅

—

WeChat Official Account

—

✅ (with theme preview)

Images (in-document)

✅ (paste / drag-drop)

✅ (bundled in ZIP)

API · MCP · CLI

Three channels, one contract:

  • REST API: OpenAPI 3.1 spec, Bearer Token auth, ETag versioning, cursor pagination, idempotency keys

  • MCP server: doco mcp (ships inside doco-agent-cli) — 29 tools plus doco:// resources

  • doco CLI: login / whoami / docs / blocks / edit / mcp, global --json, writes internalize ETag/If-Match

Turn your docs into programmable assets — script your own backups, let an agent organize your knowledge base, pipe docs from your publishing workflow to your blog. Built-in API documentation page, ready to use out of the box.

Tech Stack

Layer

Technology

Frontend Framework

React 18 + Vite + TypeScript

CSS

Tailwind CSS v4

Editor

Tiptap v3 (ProseMirror)

Collaboration

Yjs (CRDT) + Hocuspocus

Diagrams

Mermaid + PlantUML

Backend

Node.js + Express + Hocuspocus Server

Database

better-sqlite3 (SQLite, WAL mode)

UI Components

Radix UI, Lucide React, Tippy.js

Quick Start

Prerequisites

  • Node.js >= 22

  • pnpm

Install & Run

# Install frontend dependencies
pnpm install

# Install backend dependencies
cd backend && npm install && cd ..

# Start the frontend dev server (Vite, default :5173)
pnpm run dev

# In another terminal, start the backend (Express + WebSocket, default :8000)
cd backend
npm run dev

Open http://localhost:5173 — it will auto-connect to the backend WebSocket service.

The complete self-hosted package includes a Caddy frontend, Node.js collaboration backend, persistent SQLite storage, health checks, and a same-origin WebSocket proxy. The public images support both linux/amd64 and linux/arm64.

git clone https://github.com/songofhawk/doco.git
cd doco
cp .env.docker.example .env.docker

# Review .env.docker first, then start with prebuilt Docker Hub images
docker compose --env-file .env.docker up -d

# Verify the deployment
docker compose --env-file .env.docker ps
curl --fail http://localhost:8080/healthz

Open http://localhost:8080 by default. Set ALLOWED_ORIGINS, COOKIE_SECURE, Google OAuth, and SMTP values in .env.docker for your environment. These values are injected when the containers start and are not baked into the images. Application data is stored in the doco-data named volume.

Docker Hub: songofhawkg/doco-frontend · songofhawkg/doco-backend

To build the same images from source instead:

docker compose --env-file .env.docker up -d --build

See the Docker deployment guide for all configuration options, HTTPS, logs, backup, restore, and upgrades. Do not run docker compose down -v unless you intend to delete the database and attachments.

Manual Build & Deployment

# Frontend build
pnpm run build          # output → dist/
pnpm run deploy         # deploy to Cloudflare Pages

# Backend (production)
cd backend
npm start

Project Structure

doco/
├── src/
│   ├── main.tsx                      # App entry point
│   ├── App.tsx                       # Root component, routing, import/export
│   ├── components/
│   │   └── Sidebar.tsx               # KB sidebar (document tree)
│   └── editor/                       # Editor module
│       ├── index.ts                  # Entry, exports DocoEditor component
│       ├── DocoEditor.tsx            # Editor core (Yjs/Hocuspocus init, extension registration)
│       ├── types.ts                  # DocoEditor Props/Ref type definitions
│       └── components/
│           ├── BubbleMenu.tsx        # Selection floating toolbar
│           ├── BlockHandle.tsx       # Block drag handle
│           ├── SlashCommand.ts       # / command palette
│           ├── CommandList.tsx       # Command palette UI
│           ├── suggestions.ts        # Command menu data
│           ├── CollapseExtension.ts  # Block collapse extension
│           ├── DocSettings.tsx       # Document settings (heading numbering, background)
│           ├── MermaidBlock.ts       # Mermaid node definition
│           ├── MermaidComponent.tsx  # Mermaid renderer
│           ├── PlantUMLBlock.ts      # PlantUML node definition
│           ├── PlantUMLComponent.tsx # PlantUML renderer
│           ├── CalloutBlock.ts       # Callout block definition
│           ├── CalloutComponent.tsx  # Callout renderer
│           ├── SpreadsheetBlock.ts   # Spreadsheet node definition
│           ├── SpreadsheetComponent.tsx  # Spreadsheet renderer
│           ├── spreadsheetEngine.ts  # Spreadsheet calculation engine
│           ├── WeChatExportDialog.tsx # WeChat Official Account export
│           ├── KeyboardShortcuts.ts  # Keyboard shortcuts
│           ├── TableOfContents.tsx   # Table of contents
│           ├── CodeBlockComponent.tsx # Code block (highlight + copy)
│           └── ImageComponent.tsx    # Image renderer
├── backend/
│   ├── server.js                     # Entry: Express + Hocuspocus + export routes
│   ├── database.js                   # better-sqlite3 init & schema
│   ├── api.js                        # KB / folder / document REST API
│   ├── auth.js                       # Auth (OAuth + Email + API Token)
│   ├── markdown.js                   # YDoc → Markdown server-side export
│   ├── permissions.js                # Permission management
│   ├── quota.js                      # Quota management
│   ├── openapi.js                    # OpenAPI spec definition
│   └── tests/                        # Backend tests
└── docs/                             # Design docs & proposals

Standalone Frontend Component

The editor core is also published as doco-text-editor. It contains the full Doco editing experience and built-in styles, but has no dependency on Doco authentication, REST APIs, collaboration services, or IndexedDB. The host application decides whether content lives in memory, browser storage, its own backend, or an external system such as ClickUp.

npm install doco-text-editor
import { useRef } from 'react'
import {
  DocoTextEditor,
  type DocoTextEditorRef,
} from 'doco-text-editor'
import 'doco-text-editor/style.css'

const editorRef = useRef<DocoTextEditorRef>(null)

<DocoTextEditor
  ref={editorRef}
  defaultValue="# Browser-only draft"
  format="markdown"
  onChange={({ steps }) => {
    // Only the ProseMirror steps changed by this transaction.
    queueIncrementalChanges(steps)
  }}
/>

// Read the complete document only when needed.
const json = editorRef.current?.getContent('tiptap-json')
const markdown = editorRef.current?.getContent('markdown')
const html = editorRef.current?.getContent('html')
const text = editorRef.current?.getContent('text')

The package includes headings, inline formatting, blockquotes, ordered/unordered/task lists, code blocks, images, tables, callouts, Mermaid, optional PlantUML rendering, and embedded spreadsheets. See src/editor/README.md for the complete API and integration notes.

Full Doco Editor Component Usage

import { DocoEditor } from './editor'
import type { DocoEditorRef } from './editor/types'

const editorRef = useRef<DocoEditorRef>(null)

<DocoEditor
  ref={editorRef}
  docId="doc-001"
  userId="user-001"
  collaboration={{
    websocketUrl: 'ws://localhost:8000',
  }}
  onTitleChange={(docId, title) => console.log('Title changed:', title)}
  placeholder="Start writing…"
/>

{/* Call export methods via ref */}
<button onClick={() => editorRef.current?.exportMarkdown()}>Export MD</button>

Collaboration Architecture

Browser IndexedDB (y-indexeddb)  ← local primary store
       ↕
Browser Y.Doc  ← @hocuspocus/provider (WebSocket)
       ↕  Yjs binary delta messages
Server @hocuspocus/server  →  SQLite ydoc_state (one merged snapshot per doc)
  • The browser IndexedDB is the primary store; the server snapshot is auxiliary. If the server snapshot is lost, simply open the document in the browser to repopulate it.

  • Offline editing works seamlessly; changes sync automatically when the network returns.

  • Collaborative cursors: supported by the framework, not enabled by default.

Markdown Export

Both single documents and KB bundles support Markdown export, generated on-the-fly from YDoc on the server:

# Single document export
curl http://localhost:8000/api/docs/{id}/export.md

# KB ZIP bundle
curl http://localhost:8000/api/kb/{id}/export.zip

Custom nodes (Mermaid, PlantUML, Callout, etc.) have corresponding serialization rules in backend/markdown.js. When adding new custom nodes, update the server-side serializer accordingly.

Lossless Doco Transfer

Use Export Doco File in a document, folder, or knowledge-base menu. The resulting .doco.zip contains the original Yjs state, hierarchy, document settings, standalone spreadsheets, and attachments. Importing always creates a copy with fresh resource and attachment IDs, so it can safely move between independent Doco deployments without colliding with existing data.

Use the upload button beside the knowledge-base heading to import a whole knowledge base. To import a document or folder package, choose Import Doco File from the destination knowledge base or folder menu.

License

MIT

Available Tools

29 tools
doco_batch_editDoco Batch EditA
Destructive

单事务批量编辑(1–100 个操作,全有或全无):operations 为 {op: insert|replace|delete, ...} 数组。base_version 必填语义由服务端强制(不填自动读取)。

Inputs: operations (required), document_id (required), base_version, idempotency_key. Returns: object

doco_changesDoco ChangesA
Read-onlyIdempotent

建立或增量读取文档的块级变更水位。首次不传 after 返回 manifest 与 cursor;后续传 cursor 获取 added/removed/modified/moved。sync_required=true 时必须重新读取正文并建立新基线。

Inputs: document_id (required), after, limit, locale. Returns: object

doco_conceptsDoco ConceptsA
Read-onlyIdempotent

统一读取概念层:列出/获取显式概念、沿概念关系遍历,或列出待审核候选。候选与显式概念严格分离。

Inputs: action (required), q, limit, cursor, +6 more. Returns: object

doco_create_documentDoco Create DocumentA

新建文档,可同时灌入初始正文(content: {format: markdown|tiptap-json|html, content|document})

Inputs: title (required), content, folder_id, idempotency_key, +1 more. Returns: object

doco_create_relationDoco Create RelationA

从稳定来源块创建一条显式、可审计的类型关系。target_uri 使用 doco://doc/{id}#block={block_id};创建前服务端校验两端权限与块存在性。

Inputs: target_uri (required), source_block_id (required), source_document_id (required), predicate, +2 more. Returns: object

doco_delete_blockDoco Delete BlockC
DestructiveIdempotent

删除单个块

Inputs: block_id (required), document_id (required), base_version. Returns: object

doco_edit_conceptsDoco Edit ConceptsA
Destructive

统一写入显式概念:创建、更新、补来源/关系、合并,以及接受/拒绝候选。客户端自动读取 ETag、发送 If-Match,并为每次写入生成幂等键。

Inputs: action (required), name, reason, status, +9 more. Returns: object

doco_get_blocksDoco Get BlocksA
Read-onlyIdempotent

按块读取文档:返回顶层(或 recursive=true 时全部)块及其稳定 block_id 与 version

Inputs: document_id (required), recursive. Returns: object

doco_get_documentDoco Get DocumentA
Read-onlyIdempotent

读取整篇文档正文。format: markdown(读懂语义,附带 warnings 降级提示)/ tiptap-json(无损,精确编辑用)/ html。返回含 version(后续写入需携带)。annotate=anchors 时 markdown 每个顶层块带 锚点,改完整篇写回(PUT content format=markdown)可按锚点保留未改动块的 ID。

Inputs: document_id (required), format, locale, annotate. Returns: object

doco_get_treeDoco Get TreeA
Read-onlyIdempotent

获取一个知识库的完整目录树(文件夹 + 文档)

Inputs: kb_id (required). Returns: object

doco_insert_blocksDoco Insert BlocksA

在指定位置插入一个或多个块。position 六选一:after_block_id / before_block_id / parent_block_id(+child_index) / document_start / document_end / after_heading(按标题文本定位,服务端匹配)

Inputs: nodes (required), position (required), document_id (required), base_version. Returns: object

doco_list_documentsDoco List DocumentsA
Read-onlyIdempotent

按知识库 / 文件夹 / 关键词搜索文档列表(分页)

Inputs: q, limit, cursor, locale, +3 more. Returns: object

doco_list_knowledge_basesDoco List Knowledge BasesA
Read-onlyIdempotent

列出当前用户可见的全部知识库

Inputs: no named parameters. Returns: object

doco_outlineDoco OutlineA
Read-onlyIdempotent

读取文档结构大纲:每个标题以稳定 block_id、heading_path 和顶层块区间表达,适合先规划再局部读取。

Inputs: document_id (required). Returns: object

doco_patch_blockDoco Patch BlockA
Idempotent

更新单个块:提供完整 node 替换,或用 attrs/content 局部修改。带 base_version 做乐观并发校验;不带则自动读取最新版本。409 时请重读合并重试。

Inputs: block_id (required), document_id (required), node, attrs, +2 more. Returns: object

doco_readDoco ReadA
Read-onlyIdempotent

按 token 预算局部读取文档。可用 around 锚定任意嵌套 block_id,或用 next_cursor 续读;游标绑定正文版本,read_cursor_stale 时必须重新规划。

Inputs: document_id (required), view, around, cursor, +4 more. Returns: object

doco_rebuild_summaryDoco Rebuild SummaryA
Idempotent

重建摘要。deterministic 同步返回可追溯 fallback;model 只创建异步任务,必须随后用 doco_summary(job_id) 查询,来源变化或摘要被钉住时结果会 obsolete 而不会覆盖。

Inputs: target_id (required), scope, block_id, generator, +2 more. Returns: object

doco_refresh_concept_candidatesDoco Refresh Concept CandidatesA
Idempotent

触发概念候选抽取/刷新。只生成待审核候选,不会直接污染显式概念层;写入自动带幂等键。

Inputs: document_id (required). Returns: object

doco_review_translation_unitDoco Review Translation UnitA
Idempotent

在具体目标语言文档上确认当前译文或忽略单元;不接受机器结果直接覆盖,遇到 409 必须重读目标版本。

Inputs: unit_id (required), document_id (required), review_status (required), target_document_id (required), +2 more. Returns: object

doco_save_summaryDoco Save SummaryA
Idempotent

人工编辑并可钉住摘要。客户端会先读取当前 summary/source 双版本后带保护写入;pinned 只防自动覆盖,来源变化后仍会显示 stale。

Inputs: summary (required), target_id (required), scope, pinned, +3 more. Returns: object

doco_search_v2Doco Search V2A
Read-onlyIdempotent

带查询级完整性证明的全文搜索。返回目录路径、标题路径、前后文、分数解释、source/indexed 水位;exhaustive 模式可用 cursor 完整遍历。projection.complete=false 时结果不完整,不能据此断言“知识不存在”。

Inputs: q (required), mode, limit, cursor, +2 more. Returns: object

doco_summaryDoco SummaryA
Read-onlyIdempotent

读取章节、文档、文件夹或知识库摘要,或查询异步生成任务。结果带来源块、来源版本、覆盖率、freshness 与 fallback 语义;模型关闭时仍始终可用。

Inputs: query, scope, job_id, block_id, +1 more. Returns: object

doco_translationsDoco TranslationsB
Read-onlyIdempotent

读取一个具体文档的 Document Set、源语言、可用语言版本和每种语言的新鲜度。

Inputs: document_id (required). Returns: object

doco_translation_unitsDoco Translation UnitsA
Read-onlyIdempotent

读取目标语言的块级翻译单元、稳定块映射、source_version 和 missing/current/conflict 等状态。

Inputs: document_id (required), locale, status, target_document_id. Returns: object

doco_traverseDoco TraverseA
Read-onlyIdempotent

沿显式关系遍历文档。返回正向/反向关系、注册谓词、来源块与来源版本;status=dangling_* 时目标已失效,evidence_freshness=stale 时应重新确认证据,projection_freshness=stale 时不得当作完整关系图。

Inputs: document_id (required), direction, predicate. Returns: object

doco_update_documentDoco Update DocumentA
DestructiveIdempotent

整篇写回文档正文。markdown 写回凭 锚点保留未改动块 ID(配合 doco_get_document 的 annotate=anchors);tiptap-json 为整篇无损替换。强烈建议带 base_version,409 时重读合并重试。

Inputs: format (required), document_id (required), content, document, +1 more. Returns: object

doco_upload_attachmentDoco Upload AttachmentA

上传附件(图片/PDF/文本/Word),返回 attachment_id 与 URL;在文档块中用 image 节点 attrs.attachmentId 引用

Inputs: file_path (required), document_id (required), filename, idempotency_key. Returns: object

doco_whoamiDoco WhoamiA
Read-onlyIdempotent

自检身份:当前 Token 对应的用户与权限 scope

Inputs: no named parameters. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.6/5.0

    Scored across 29 tools

    Disambiguation4/5

    Most tools map to a distinct resource and action (documents, blocks, translations, relations, concepts, summaries), so an agent can generally pick the right one. The main ambiguities are doco_search vs. doco_search_v2 and the easily confused doco_translations vs. doco_translation_units, though the descriptions give enough detail to resolve them.

    Naming Consistency4/5

    The doco_ prefix, snake_case, and familiar verb_noun forms (list_, get_, create_, update_, delete_, insert_, patch_) give the set a strong, predictable pattern. A few noun-only readers (doco_changes, doco_outline, doco_concepts, doco_summary) and the doco_search_v2 suffix are minor deviations.

    Tool Count3/5

    29 tools is a large surface, but the server covers many distinct subdomains: knowledge-base navigation, document/block editing, search, translations, relations, concepts, summaries, and attachments. Still, the count is heavy and some functions (search_v2, separate block readers) could plausibly be merged, so the set is borderline rather than tightly scoped.

    Completeness3/5

    The tool set covers document create/read/update, block-level editing, search, summaries, concepts, and translations, so most core workflows are supported. Notable gaps are the lack of document deletion, relation deletion, and attachment lifecycle operations (list/download/delete), which agents cannot work around easily.

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      B
      maintenance
      MCP server that gives AI coding agents on-demand access to private project docs via BM25 ranked search. One setup for Claude Code, Cursor, Codex, Gemini CLI, and more. Docs stay private, never in public repos.
      15
      16
      Apache 2.0
    • A
      license
      Not graded
      quality
      D
      maintenance
      Persistent docs and memory for AI agents. Writespace is a collaborative markdown editor with a built-in MCP server — your model reads, writes, organizes, and searches a shared workspace while humans edit the same docs live. Drop the ranked full-text search straight in as RAG retrieval.
      MIT
    • A
      license
      Not graded
      quality
      F
      maintenance
      MCP server for collaborative markdown editing, allowing agents to write documents and humans to comment, with comments fed back as agent input.
      377,398 npm
      MIT