mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-11 07:06:28 +08:00
0da91e406e
## Summary This PR adds write capabilities to AFFiNE's MCP (Model Context Protocol) integration, enabling external tools (Claude, GPT, etc.) to create and modify documents programmatically. **New MCP Tools:** - `create_document` - Create new documents from markdown content - `update_document` - Update document content using structural diffing for minimal changes (preserves document history and enables real-time collaboration) **Implementation:** - `markdown_to_ydoc.rs` - Converts markdown to AFFiNE-compatible y-octo binary format - `markdown_utils.rs` - Shared markdown parsing utilities (used by both ydoc-to-md and md-to-ydoc) - `update_ydoc.rs` - Structural diffing implementation for updating existing documents - `DocWriter` service - TypeScript service for document operations - Exposes `markdownToDocBinary` and `updateDocBinary` via napi bindings **Supported Markdown Elements:** - Headings (H1-H6) - Paragraphs - Bullet lists and numbered lists - Code blocks (with language detection) - Blockquotes - Horizontal dividers - Todo items (checkboxes) **y-octo Changes:** This PR reverts the y-octo sync (ca2462f,a5b60cf) which introduced a concurrency bug causing hangs when creating documents with many nested block structures. It also ports the improved `get_node_index` binary search fix from upstream that prevents divide-by-zero panics when decoding documents. ## Test Results ✅ ### Unit Tests (47/47 passing) | Test Suite | Tests | Status | |------------|-------|--------| | markdown_to_ydoc | 16/16 | ✅ Pass | | markdown_utils | 11/11 | ✅ Pass | | update_ydoc | 13/13 | ✅ Pass | | delta_markdown | 2/2 | ✅ Pass | | affine (doc parser) | 5/5 | ✅ Pass | ### End-to-End MCP Testing ✅ Tested against local AFFiNE server with real MCP client requests: | Tool | Result | Notes | |------|--------|-------| | `tools/list` | ✅ Pass | Returns all 5 tools with correct schemas | | `create_document` | ✅ Pass | Successfully created test documents | | `update_document` | ✅ Pass | Successfully updated documents with structural diffing | | `read_document` | ✅ Pass | Existing tool, works correctly | | `keyword_search` | ✅ Pass | Existing tool, works correctly | **E2E Test Details:** - Started local AFFiNE server with PostgreSQL, Redis, and Manticore - Created test user and workspace via seed/GraphQL - Verified MCP endpoint at `/api/workspaces/:workspaceId/mcp` - Tested JSON-RPC calls with proper SSE streaming - Confirmed documents are stored and indexed correctly (verified via server logs) ## Test Plan - [x] All Rust unit tests pass (47 tests) - [x] Native bindings build successfully (release mode) - [x] Document creation via MCP works end-to-end - [x] Document update via MCP works end-to-end - [x] CodeRabbit feedback addressed - [ ] Integration testing with Claude/GPT MCP clients Closes #14161 --- **Requested by:** @realies **Key guidance from:** @darkskygit (use y-octo instead of yjs for memory efficiency) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Create documents from Markdown: generate new documents directly from Markdown content with automatic title extraction * Update documents with Markdown: modify existing documents using Markdown as the source with automatic diff calculation for efficient updates * Copilot integration: new tools for document creation and updates through Copilot's interface <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
97 lines
2.8 KiB
TOML
97 lines
2.8 KiB
TOML
[package]
|
|
edition = "2024"
|
|
license-file = "LICENSE"
|
|
name = "affine_common"
|
|
version = "0.1.0"
|
|
|
|
[features]
|
|
default = []
|
|
doc-loader = [
|
|
"docx-parser",
|
|
"infer",
|
|
"path-ext",
|
|
"pdf-extract",
|
|
"readability",
|
|
"serde",
|
|
"serde_json",
|
|
"strum_macros",
|
|
"text-splitter",
|
|
"thiserror",
|
|
"tiktoken-rs",
|
|
"tree-sitter",
|
|
"url",
|
|
]
|
|
hashcash = ["chrono", "sha3", "rand"]
|
|
tree-sitter = [
|
|
"cc",
|
|
"dep:tree-sitter",
|
|
"dep:tree-sitter-c",
|
|
"dep:tree-sitter-c-sharp",
|
|
"dep:tree-sitter-cpp",
|
|
"dep:tree-sitter-go",
|
|
"dep:tree-sitter-java",
|
|
"dep:tree-sitter-javascript",
|
|
"dep:tree-sitter-kotlin-ng",
|
|
"dep:tree-sitter-python",
|
|
"dep:tree-sitter-rust",
|
|
"dep:tree-sitter-scala",
|
|
"dep:tree-sitter-typescript",
|
|
]
|
|
ydoc-loader = [
|
|
"assert-json-diff",
|
|
"nanoid",
|
|
"pulldown-cmark",
|
|
"serde",
|
|
"serde_json",
|
|
"thiserror",
|
|
"y-octo",
|
|
]
|
|
|
|
[dependencies]
|
|
assert-json-diff = { workspace = true, optional = true }
|
|
chrono = { workspace = true, optional = true }
|
|
docx-parser = { workspace = true, optional = true }
|
|
infer = { workspace = true, optional = true }
|
|
nanoid = { workspace = true, optional = true }
|
|
path-ext = { workspace = true, optional = true }
|
|
pdf-extract = { workspace = true, optional = true }
|
|
pulldown-cmark = { workspace = true, optional = true }
|
|
rand = { workspace = true, optional = true }
|
|
readability = { workspace = true, optional = true, default-features = false }
|
|
serde = { workspace = true, optional = true, features = ["derive"] }
|
|
serde_json = { workspace = true, optional = true }
|
|
sha3 = { workspace = true, optional = true }
|
|
strum_macros = { workspace = true, optional = true }
|
|
text-splitter = { workspace = true, features = [
|
|
"markdown",
|
|
"tiktoken-rs",
|
|
], optional = true }
|
|
thiserror = { workspace = true, optional = true }
|
|
tiktoken-rs = { workspace = true, optional = true }
|
|
tree-sitter = { workspace = true, optional = true }
|
|
tree-sitter-c = { workspace = true, optional = true }
|
|
tree-sitter-c-sharp = { workspace = true, optional = true }
|
|
tree-sitter-cpp = { workspace = true, optional = true }
|
|
tree-sitter-go = { workspace = true, optional = true }
|
|
tree-sitter-java = { workspace = true, optional = true }
|
|
tree-sitter-javascript = { workspace = true, optional = true }
|
|
tree-sitter-kotlin-ng = { workspace = true, optional = true }
|
|
tree-sitter-python = { workspace = true, optional = true }
|
|
tree-sitter-rust = { workspace = true, optional = true }
|
|
tree-sitter-scala = { workspace = true, optional = true }
|
|
tree-sitter-typescript = { workspace = true, optional = true }
|
|
url = { workspace = true, optional = true }
|
|
y-octo = { workspace = true, optional = true }
|
|
|
|
[dev-dependencies]
|
|
criterion = { workspace = true }
|
|
rayon = { workspace = true }
|
|
tempfile = "3"
|
|
|
|
[build-dependencies]
|
|
cc = { version = "1", optional = true }
|
|
|
|
[[bench]]
|
|
harness = false
|
|
name = "hashcash"
|