/** * ═══════════════════════════════════════════════════════════ * AGE OS · MCP 工具: Notion 操作 * ═══════════════════════════════════════════════════════════ * * 签发: 铸渊 · ICE-GL-ZY001 * 版权: 国作登字-2026-A-00037559 * * 提供 5 个 Notion MCP 工具: * notionQuery — 查询 Notion 数据库 * notionReadPage — 读取 Notion 页面内容 * notionWritePage — 创建 Notion 页面 * notionUpdatePage — 更新 Notion 页面属性 * notionWriteSyslog — 写入系统日志到 Notion */ 'use strict'; const notion = require('../notion-client'); /** * notionQuery — 查询 Notion 数据库 * * input: * database_id: string — 数据库 ID(或别名: changelog / receipt / syslog) * filter: object — Notion 过滤器(可选) * sorts: object[] — 排序规则(可选) * page_size: number — 每页数量(可选,默认20) * start_cursor: string — 分页游标(可选) */ async function notionQuery(input) { const { database_id, filter, sorts, page_size, start_cursor } = input; if (!database_id) throw new Error('缺少 database_id'); // 支持别名 const dbId = resolveDbAlias(database_id); const result = await notion.queryDatabase(dbId, filter, sorts, page_size || 20, start_cursor); return { count: result.results.length, has_more: result.has_more, next_cursor: result.next_cursor, items: result.results.map(simplifyPage) }; } /** * notionReadPage — 读取 Notion 页面完整内容 * * input: * page_id: string — 页面 ID */ async function notionReadPage(input) { const { page_id } = input; if (!page_id) throw new Error('缺少 page_id'); const page = await notion.readPage(page_id); return { id: page.id, created_time: page.created_time, last_edited_time: page.last_edited_time, properties: simplifyProperties(page.properties), blocks_count: page.blocks.length, blocks: page.blocks.map(simplifyBlock) }; } /** * notionWritePage — 在数据库中创建新页面 * * input: * database_id: string — 数据库 ID(或别名) * title: string — 页面标题 * properties: object — 额外属性(Notion API 格式,可选) * content: string — 页面正文内容(可选,自动转为段落块) */ async function notionWritePage(input) { const { database_id, title, properties, content } = input; if (!database_id) throw new Error('缺少 database_id'); if (!title) throw new Error('缺少 title'); const dbId = resolveDbAlias(database_id); // 构建属性 const pageProps = { ...(properties || {}), '标题': { title: [{ text: { content: title } }] } }; // 构建内容块 const children = []; if (content) { // 按段落分割 const paragraphs = content.split('\n\n'); for (const para of paragraphs) { if (para.trim()) { children.push({ object: 'block', type: 'paragraph', paragraph: { rich_text: [{ text: { content: para.trim() } }] } }); } } } return notion.createPage(dbId, pageProps, children); } /** * notionUpdatePage — 更新 Notion 页面属性 * * input: * page_id: string — 页面 ID * properties: object — 要更新的属性(Notion API 格式) */ async function notionUpdatePage(input) { const { page_id, properties } = input; if (!page_id) throw new Error('缺少 page_id'); if (!properties) throw new Error('缺少 properties'); return notion.updatePage(page_id, properties); } /** * notionWriteSyslog — 写入系统日志到 Notion * * input: * level: string — 日志级别: info / warning / error / critical * source: string — 来源: zhuyuan / agent / workflow / mcp * message: string — 日志内容 * details: object — 附加详情(可选) */ async function notionWriteSyslog(input) { const { level, source, message, details } = input; if (!level) throw new Error('缺少 level'); if (!source) throw new Error('缺少 source'); if (!message) throw new Error('缺少 message'); return notion.writeSyslog(level, source, message, details); } // ═══════════════════════════════════════════════════════════ // 辅助函数 // ═══════════════════════════════════════════════════════════ /** * 解析数据库别名 */ function resolveDbAlias(id) { const aliases = { changelog: notion.NOTION_CONFIG.databases.changelog, receipt: notion.NOTION_CONFIG.databases.receipt, syslog: notion.NOTION_CONFIG.databases.syslog, cognition_runtime: notion.NOTION_CONFIG.databases.cognition_runtime, dev_workorder: notion.NOTION_CONFIG.databases.dev_workorder }; return aliases[id] || id; } /** * 简化页面对象(去除 Notion API 冗余结构) */ function simplifyPage(page) { return { id: page.id, created_time: page.created_time, last_edited_time: page.last_edited_time, properties: simplifyProperties(page.properties) }; } /** * 简化属性(提取值) */ function simplifyProperties(props) { if (!props) return {}; const result = {}; for (const [name, prop] of Object.entries(props)) { result[name] = extractPropertyValue(prop); } return result; } /** * 提取 Notion 属性值 */ function extractPropertyValue(prop) { switch (prop.type) { case 'title': return prop.title.map(t => t.plain_text).join(''); case 'rich_text': return prop.rich_text.map(t => t.plain_text).join(''); case 'number': return prop.number; case 'select': return prop.select?.name || null; case 'multi_select': return prop.multi_select.map(s => s.name); case 'date': return prop.date?.start || null; case 'checkbox': return prop.checkbox; case 'url': return prop.url; case 'email': return prop.email; case 'status': return prop.status?.name || null; case 'formula': return prop.formula?.string || prop.formula?.number || null; case 'relation': return prop.relation.map(r => r.id); default: return `[${prop.type}]`; } } /** * 简化内容块 */ function simplifyBlock(block) { const simplified = { id: block.id, type: block.type, has_children: block.has_children }; const content = block[block.type]; if (content) { if (content.rich_text) { simplified.text = content.rich_text.map(t => t.plain_text).join(''); } if (content.language) { simplified.language = content.language; } } return simplified; } module.exports = { notionQuery, notionReadPage, notionWritePage, notionUpdatePage, notionWriteSyslog };