add_child_note
Add a child note to a Zotero literature entry. Supports HTML and plain text. Requires closing Zotero before writing and refreshing afterward.
Instructions
为 Zotero 文献条目创建子笔记。
支持 HTML 和纯文本。写入后需重启 Zotero 或按 Ctrl+Shift+R 刷新。 注意:写操作需要关闭 Zotero 桌面应用。
Args: parent_item_id: 父文献条目的 itemID(数字字符串) note_content: 笔记内容(HTML 或纯文本,支持 Markdown 风格)
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| parent_item_id | Yes | ||
| note_content | Yes |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- annota/server.py:139-167 (handler)Registration + handler for the 'add_child_note' tool. The @mcp.tool() decorator registers it as an MCP tool. The function takes parent_item_id and note_content, then delegates to zotero_db.create_child_note().
# ── Tool 3: add_child_note ────────────────────────────────────── @mcp.tool() def add_child_note(parent_item_id: str, note_content: str) -> str: """为 Zotero 文献条目创建子笔记。 支持 HTML 和纯文本。写入后需重启 Zotero 或按 Ctrl+Shift+R 刷新。 注意:写操作需要关闭 Zotero 桌面应用。 Args: parent_item_id: 父文献条目的 itemID(数字字符串) note_content: 笔记内容(HTML 或纯文本,支持 Markdown 风格) """ try: result = zotero_db.create_child_note( parent_item_id=int(parent_item_id), note_content=note_content, ) return json.dumps({ **result, "message": "笔记已写入 Zotero 数据库。请重启 Zotero 或按 Ctrl+Shift+R 刷新查看。", }, ensure_ascii=False) except sqlite3.OperationalError as e: if "locked" in str(e).lower(): return json.dumps({ "error": "database_locked", "message": "Zotero 数据库被锁定,请先关闭 Zotero 桌面应用再重试。", }, ensure_ascii=False) raise - annota/zotero_db.py:500-555 (helper)Helper function create_child_note() that performs the actual Zotero database write. It inserts a new item with itemTypeID=28 (note), generates a random Zotero key, wraps content in HTML if needed, extracts a title, and inserts into itemNotes table.
@_retry_on_lock def create_child_note( parent_item_id: int, note_content: str, ) -> dict: """为 Zotero 条目创建子笔记。 Args: parent_item_id: 父文献条目的 itemID note_content: HTML 格式的笔记内容(Zotero 原生使用 HTML) Returns: {"itemID": int, "key": str} """ key = generate_key() now = _now_iso() # 如果不是 HTML,包裹为简单 HTML if not note_content.strip().startswith("<"): note_content = f"<p>{note_content}</p>" # 提取前50字符作为标题 import re title = re.sub(r"<[^>]+>", "", note_content)[:50].strip() conn = _connect() try: cursor = conn.cursor() # 插入 items 记录 cursor.execute(""" INSERT INTO items (itemTypeID, dateAdded, dateModified, clientDateModified, libraryID, key, version, synced) VALUES (?, ?, ?, ?, ?, ?, 0, 0) """, (ITEM_TYPE_NOTE, now, now, now, LIBRARY_ID, key)) item_id = cursor.lastrowid # 插入 itemNotes 记录 cursor.execute(""" INSERT INTO itemNotes (itemID, parentItemID, note, title) VALUES (?, ?, ?, ?) """, (item_id, parent_item_id, note_content, title)) conn.commit() logger.info( "创建笔记: itemID=%d, key=%s, parentID=%d, title=%s", item_id, key, parent_item_id, title, ) return {"itemID": item_id, "key": key} except Exception: conn.rollback() raise finally: conn.close() - annota/config.py:27-32 (schema)Schema constant: ITEM_TYPE_NOTE = 28 — the itemTypeID used for note items in the Zotero database schema.
# itemTypeID 映射 ITEM_TYPE_ANNOTATION = 1 ITEM_TYPE_ATTACHMENT = 3 ITEM_TYPE_NOTE = 28 # 批注类型 (itemAnnotations.type) - annota/server.py:383-389 (registration)MCP server entry point that runs the server (transport='stdio'), making all registered tools including add_child_note available.
# ── 入口 ──────────────────────────────────────────────────────── if __name__ == "__main__": logger.info("Annota MCP Server 启动中...") logger.info("Zotero 数据库: %s", ZOTERO_DB_PATH) logger.info("Zotero 存储目录: %s", ZOTERO_STORAGE_DIR) mcp.run(transport="stdio") - annota/zotero_db.py:109-112 (helper)Helper function _now_iso() that generates ISO-formatted timestamps used when creating notes.
def _now_iso() -> str: """返回 Zotero 使用的 ISO 时间戳格式。""" return datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")