Self-hosted, token-scoped Model Context Protocol (MCP) server for AppFlowy
Strong foundation with 16 well-named tools covering AppFlowy's core operations. All tools have descriptions (avg 180 chars) and input schemas with typed parameters. Naming follows verb_noun convention consistently (get_, create_, update_, delete_). However, several parameter descriptions lack actionable constraints (e.g., 'depth' has no min/max bounds), and output schemas are not documented. Error handling is minimal, no recovery guidance or categorization. Missing tool annotations (readOnlyHint, destructiveHint) despite clear risk levels. Some descriptions reference complex nested structures (page_data, delta) without formal schema constraints.
Append blocks to the END of an existing page (cannot edit existing blocks — use 'Edit block text'/'Insert block' for that). Args: workspace_id: The workspace containing the page. page_id: The page to append to; must be within your scope. blocks_to_add: List of block objects like {"type": "paragraph", "data": {"delta": [{"insert": "Hello"}]}}.
Create a new row in a database. Args: workspace_id: The workspace containing the database. database_id: The database to write to; must be within your scope. cells: Dict of field name-or-id -> value.
Create a new page under a parent view you have access to. Args: workspace_id: The workspace to create in. parent_view_id: The parent view/folder; must be within your scope. name: Optional page title. view_layout: 0=Document, 1=Grid, 2=Board, 3=Calendar. page_data: Optional initial content, AppFlowy page-data format, e.g. {"type": "page", "children": [ {"type": "paragraph", "data": {"delta": [{"insert": "Hi"}]}}]}. Block types: paragraph, heading (data.level), bulleted_list, numbered_list, todo_list, divider, image (data.url). delta inserts accept attributes: bold, italic, underline, strikethrough, code, color, href.
Delete an existing block from a page. Args: workspace_id: The workspace containing the page. page_id: The page containing the block; must be within your scope. block_id: The block to delete (from 'Get page blocks').
Output schemas not documented. LLMs cannot plan downstream calls or extract required fields (e.g., what does get_workspace_list return? What fields are in each workspace object?). Blocks tool chaining and forces agents to guess response structure.
Numeric parameters lack bounds. 'depth' in get_workspace_folder defaults to 10 but has no min/max documented. 'index' in insert_block has no bounds. Unbounded numbers let LLMs pass absurd values (depth=999999) that break APIs or cause timeouts.
Tool annotations missing. Tools marked DESTRUCTIVE (delete_block, delete_database_row) and WRITE operations lack readOnlyHint/destructiveHint/idempotentHint annotations. Agents cannot distinguish safe reads from irreversible mutations without reading descriptions.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 66 | 2026-07-28+ | v2 |
Delete a row from a database. Args: workspace_id: The workspace containing the database. database_id: The database containing the row; must be within your scope. row_id: The row to delete.
Replace one existing block's content in place. Provide ``text`` (plain) or ``delta`` (rich; takes precedence). Delta ops are {"insert": "...", "attributes": {...}} with attributes bold, italic, underline, strikethrough, code, color, bg_color, href. Args: workspace_id: The workspace containing the page. page_id: The page containing the block; must be within your scope. block_id: The block to edit (from 'Get page blocks'). text: New plain text. delta: New rich text with formatting.
Get the field definitions of a database. Args: workspace_id: The workspace containing the database. database_id: The database to read; must be within your scope.
List databases in a workspace, filtered to your token's scope. Args: workspace_id: The workspace to read.
Get all rows of a database, filtered to your token's scope. Args: workspace_id: The workspace containing the database. database_id: The database to read; must be within your scope.
List a page's editable blocks in document order (id, type, text, depth). Use before 'Edit block text' / 'Delete block' / 'Insert block' to discover block ids. ``text`` is null for non-text blocks (divider, image). Args: workspace_id: The workspace containing the page. page_id: The page to read; must be within your scope.
Get full details (metadata + content) of a page within your scope. Args: workspace_id: The workspace containing the page. page_id: The page to read.
Get the page/folder tree of a workspace, pruned to your token's scope. Args: workspace_id: The workspace to read. depth: Maximum nesting depth to retrieve (default 10). root_view_id: Optional view id to use as the tree root.
List the workspaces visible to your token. The first call to make. Workspaces outside your token's scope are hidden.
Insert a new block at a specific position in the page. Args: workspace_id: The workspace containing the page. page_id: The page to insert into; must be within your scope. parent_block_id: The block to insert under (or None for root). index: Position within the parent's children (0 = first). block_type: Type of block (paragraph, heading, bulleted_list, etc.). text: Optional initial text content. delta: Optional initial rich text.
Update cells in an existing database row. Args: workspace_id: The workspace containing the database. database_id: The database containing the row; must be within your scope. row_id: The row to update. cells: Dict of field name-or-id -> new value.
Update a page's metadata (name, icon, locked state). Only provided fields change. Args: workspace_id: The workspace containing the page. page_id: The page to update; must be within your scope. name: Optional new title. icon: Optional icon object. is_locked: Optional lock/unlock. extra: Optional extra metadata.
Complex nested structures (page_data, delta, cells) documented only in prose. No formal JSON Schema constraints for block types, delta operations, or field types. LLMs cannot validate structure before calling, errors occur at runtime.
No error recovery guidance. ToolError exceptions raised (e.g., 'unauthorized', 'AccessDenied') provide no actionable next steps. LLMs receive raw error strings with no hint whether to retry, ask user, or call a different tool.