use super::skill; use serde_json::{json, Value}; pub const MANIFEST_SCHEMA_VERSION: &str = "mnote.hermes_tool_manifest.v1"; pub const TOOL_SCHEMA_VERSION: &str = "mnote.hermes_tool.v1"; pub fn manifest() -> Value { let tools = annotate_tools_with_capabilities(vec![ skill_read_tool(), context_snapshot_tool(), context_read_current_page_tool(), context_resolve_target_tool(), doc_fetch_tool(), doc_find_tool(), knowledge_rag_status_tool(), knowledge_rag_query_tool(), knowledge_rag_open_reference_tool(), block_fetch_tool(), doc_plan_update_tool(), block_replace_tool(), block_insert_after_tool(), block_delete_tool(), block_move_after_tool(), doc_apply_block_ops_tool(), doc_markdown_edit_tool(), page_get_tool(), page_save_tool(), available_tool( "mnote.page.update_title", "更新当前页面标题", ["page.write"], ), available_tool( "mnote.page.update_options", "更新当前页面设置", ["page.write"], ), mindmap_fetch_tool(), mindmap_apply_ops_tool(), mindmap_create_from_outline_tool(), office_fetch_summary_tool(), office_propose_changes_tool(), onlyoffice_session_current_tool(), onlyoffice_capabilities_tool(), onlyoffice_selection_get_tool(), onlyoffice_document_insert_text_tool(), onlyoffice_document_replace_selection_tool(), onlyoffice_document_insert_html_tool(), onlyoffice_document_export_tool(), onlyoffice_document_search_replace_tool(), onlyoffice_document_insert_table_tool(), onlyoffice_document_get_comments_tool(), onlyoffice_document_add_comment_tool(), onlyoffice_sheet_get_sheets_tool(), onlyoffice_sheet_add_sheet_tool(), onlyoffice_sheet_rename_sheet_tool(), onlyoffice_sheet_get_range_tool(), onlyoffice_sheet_get_range_values_tool(), onlyoffice_sheet_get_values_tool(), onlyoffice_sheet_set_value_tool(), onlyoffice_sheet_set_formula_tool(), onlyoffice_sheet_batch_set_values_tool(), onlyoffice_sheet_set_range_values_tool(), onlyoffice_sheet_format_range_tool(), onlyoffice_sheet_set_dimensions_tool(), onlyoffice_sheet_sort_range_tool(), onlyoffice_sheet_add_chart_tool(), onlyoffice_presentation_get_slides_tool(), onlyoffice_presentation_get_slide_texts_tool(), onlyoffice_presentation_get_shapes_tool(), onlyoffice_presentation_add_text_slide_tool(), onlyoffice_presentation_replace_text_tool(), onlyoffice_presentation_set_shape_text_tool(), onlyoffice_presentation_delete_slide_tool(), onlyoffice_presentation_add_table_tool(), onlyoffice_presentation_clear_slide_tool(), onlyoffice_presentation_add_shape_tool(), available_tool( "mnote.artifact.create_summary", "为当前页面创建或更新 AI Summary", ["artifact.write"], ), available_tool( "mnote.artifact.create_ai_note", "基于当前页面创建新的 AI Note", ["artifact.write"], ), ]); json!({ "schemaVersion": MANIFEST_SCHEMA_VERSION, "plugin": { "name": "mnote", "description": "mnote 页面、树、artifact 与 edge 工具", "runtimeOwner": "mnote-web", "writeOwner": "rust-runtime-kernel" }, "capabilities": skill::manifest_capabilities(), "tools": tools }) } fn annotate_tools_with_capabilities(tools: Vec) -> Vec { tools .into_iter() .map(|mut tool| { let name = tool.get("name").and_then(Value::as_str).unwrap_or_default(); let capability_ids = skill::capability_ids_for_tool(name); if let Value::Object(map) = &mut tool { if let Some(first) = capability_ids.first() { map.insert("capabilityId".into(), json!(first)); } if !capability_ids.is_empty() { map.insert("capabilityIds".into(), json!(capability_ids)); } } tool }) .collect() } fn skill_read_tool() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert("skillId".into(), json!({ "type": "string" })); map.insert("agentId".into(), json!({ "type": "string" })); } json!({ "name": "mnote.skill.read", "description": "按需读取 MNote skill 正文。默认 prompt 只列 skill 摘要,正文必须通过本工具懒加载。", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["skill.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "required": ["skillId"], "properties": properties } }) } fn context_snapshot_tool() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert( "contextRefs".into(), json!({ "type": "array", "items": { "type": ["string", "object"] } }), ); } json!({ "name": "mnote.context.snapshot", "description": "返回本次 run 可用的 MNote 上下文摘要,不返回页面正文全文。", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["context.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "properties": properties } }) } fn context_read_current_page_tool() -> Value { let mut tool = doc_fetch_tool(); if let Value::Object(map) = &mut tool { map.insert( "name".into(), Value::String("mnote.context.read_current_page".into()), ); map.insert( "description".into(), Value::String("在 current_page contextRef 被授权时读取当前 Markdown 页面。".into()), ); map.insert( "capabilityScope".into(), json!(["context.read", "page.read"]), ); } tool } fn context_resolve_target_tool() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert( "contextRefs".into(), json!({ "type": "array", "items": { "type": ["string", "object"] } }), ); map.insert("relativePath".into(), json!({ "type": "string" })); map.insert( "fileVersion".into(), json!({ "type": ["string", "object"] }), ); } json!({ "name": "mnote.context.resolve_target", "description": "解析当前 Page AI run 的工作区、文档、rootUri、relativePath 与 file version。", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["context.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "properties": properties } }) } fn base_identity_properties() -> Value { json!({ "workspaceId": { "type": "string" }, "documentId": { "type": "string" }, "sessionId": { "type": "string" }, "runId": { "type": "string" }, "toolCallId": { "type": "string" }, "traceId": { "type": "string" }, "actorId": { "type": "string" }, "actorType": { "type": "string" } }) } fn doc_fetch_tool() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert( "scope".into(), json!({ "type": "string", "default": "full" }), ); map.insert( "detail".into(), json!({ "type": "string", "default": "with_ids" }), ); map.insert( "format".into(), json!({ "type": "string", "enum": ["json", "markdown", "text", "page_xml"], "default": "json" }), ); map.insert("blockId".into(), json!({ "type": "string" })); map.insert( "selectedBlockIds".into(), json!({ "type": "array", "items": { "type": "string" } }), ); map.insert( "allowedTargetBlockIds".into(), json!({ "type": "array", "items": { "type": "string" } }), ); map.insert("query".into(), json!({ "type": "string" })); map.insert( "maxBlocks".into(), json!({ "type": "integer", "default": 120 }), ); } json!({ "name": "mnote.doc.fetch", "description": "读取当前页面的 canonical block projection,支持 full/outline/block/keyword 范围", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["page.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "required": ["workspaceId", "documentId", "sessionId", "runId", "toolCallId", "traceId", "actorId"], "properties": properties } }) } fn doc_find_tool() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert("query".into(), json!({ "type": "string" })); map.insert( "match".into(), json!({ "type": "string", "default": "text" }), ); map.insert("limit".into(), json!({ "type": "integer", "default": 20 })); } json!({ "name": "mnote.doc.find", "description": "在 Page Aggregate block projection 中按文本、类型或 blockId 查找块", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["page.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "required": ["workspaceId", "documentId", "sessionId", "runId", "toolCallId", "traceId", "actorId", "query"], "properties": properties } }) } #[allow(dead_code)] fn evidence_search_tool() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert("query".into(), json!({ "type": "string" })); map.insert("rootUri".into(), json!({ "type": "string" })); map.insert( "includeResources".into(), json!({ "type": "boolean", "default": true }), ); map.insert( "includeOcr".into(), json!({ "type": "boolean", "default": true }), ); map.insert( "mode".into(), json!({ "type": "string", "enum": ["keyword", "tree", "hybrid", "graph"], "default": "hybrid" }), ); map.insert("topK".into(), json!({ "type": "integer", "default": 8 })); map.insert( "scope".into(), json!({ "type": "object", "properties": { "workspaceId": { "type": "string" }, "rootUri": { "type": "string" }, "targetDocumentId": { "type": "string" }, "includeResources": { "type": "boolean" }, "includeOcr": { "type": "boolean" } } }), ); } json!({ "name": "mnote.evidence.search", "description": "搜索可回跳原文的文档证据,返回 quote、EvidenceLocator、openAction 与可直接放进最终回答的 citationMarkdown 链接。", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["evidence.read", "page.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "required": ["workspaceId", "rootUri", "query"], "properties": properties } }) } #[allow(dead_code)] fn evidence_read_tool() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert("locator".into(), json!({ "type": "object" })); map.insert( "context".into(), json!({ "type": "object", "properties": { "beforeBlocks": { "type": "integer", "default": 3 }, "afterBlocks": { "type": "integer", "default": 3 }, "includeSectionSummary": { "type": "boolean", "default": true } } }), ); } json!({ "name": "mnote.evidence.read", "description": "按 EvidenceLocator 读取原文证据及周边上下文,返回 quote/contextBlocks 与可点击引用链接供回答引用。", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["evidence.read", "page.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "required": ["locator"], "properties": properties } }) } #[allow(dead_code)] fn evidence_open_tool() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert("locator".into(), json!({ "type": "object" })); } json!({ "name": "mnote.evidence.open", "description": "把 EvidenceLocator 归一化为 MNote 可执行的打开/定位动作,并返回 citationUrl/citationMarkdown。", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["evidence.read", "page.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "required": ["locator"], "properties": properties } }) } fn knowledge_rag_status_tool() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert("rootUri".into(), json!({ "type": "string" })); } json!({ "name": "mnote.knowledge_rag.status", "description": "查看 LightRAG 资料库 provider 状态、dashboard 地址、source registry 和同步状态。", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["knowledge_rag.read", "evidence.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "properties": properties } }) } fn knowledge_rag_query_tool() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert("rootUri".into(), json!({ "type": "string" })); map.insert("query".into(), json!({ "type": "string" })); map.insert("question".into(), json!({ "type": "string" })); map.insert( "mode".into(), json!({ "type": "string", "enum": ["local", "global", "hybrid", "naive", "mix", "bypass"], "default": "mix" }), ); map.insert("topK".into(), json!({ "type": "integer", "default": 40 })); map.insert( "chunkTopK".into(), json!({ "type": "integer", "default": 20 }), ); map.insert( "includeChunkContent".into(), json!({ "type": "boolean", "default": true }), ); map.insert( "sourcePaths".into(), json!({ "type": "array", "items": { "type": "string" }, "description": "可选 MNote workspace 相对路径范围;可传文件或目录,返回 references 会限制在这些来源内。" }), ); } json!({ "name": "mnote.knowledge_rag.query", "description": "向 LightRAG 资料库提问,返回 provider 原始结果和经 MNote registry 映射后的引用。", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["knowledge_rag.read", "evidence.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "required": ["workspaceId", "rootUri", "query"], "properties": properties } }) } fn knowledge_rag_open_reference_tool() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert("rootUri".into(), json!({ "type": "string" })); map.insert("reference".into(), json!({ "type": "object" })); map.insert("referenceId".into(), json!({ "type": "string" })); map.insert("filePath".into(), json!({ "type": "string" })); map.insert("chunkId".into(), json!({ "type": "string" })); } json!({ "name": "mnote.knowledge_rag.open_reference", "description": "把 LightRAG reference 映射为 MNote 可打开的本地 resource action;没有 registry 命中时明确返回定位降级。", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["knowledge_rag.read", "evidence.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "required": ["workspaceId", "rootUri", "filePath"], "properties": properties } }) } #[allow(dead_code)] fn index_status_tool() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert("rootUri".into(), json!({ "type": "string" })); } json!({ "name": "mnote.index.status", "description": "查看本地索引范围、缓存文件、构建时间、文档数和 evidence block 数。", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["index.read", "evidence.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "required": ["workspaceId", "rootUri"], "properties": properties } }) } #[allow(dead_code)] fn index_refresh_tool() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert("rootUri".into(), json!({ "type": "string" })); } json!({ "name": "mnote.index.refresh", "description": "按当前有效索引范围重建本地搜索/evidence 缓存,不修改 Markdown 正文。", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["index.read", "evidence.read"], "status": "available", "annotations": tool_annotations(true, false, false, false), "inputSchema": { "type": "object", "required": ["workspaceId", "rootUri"], "properties": properties } }) } #[allow(dead_code)] fn index_update_settings_tool() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert("rootUri".into(), json!({ "type": "string" })); map.insert( "includePaths".into(), json!({ "type": "array", "items": { "type": "string" } }), ); map.insert( "scheduleMode".into(), json!({ "type": "string", "enum": ["manual", "daily", "weekly", "monthly"] }), ); map.insert("scheduleTime".into(), json!({ "type": "string" })); map.insert("scheduleDate".into(), json!({ "type": "string" })); map.insert("runOnChange".into(), json!({ "type": "boolean" })); map.insert("dryRun".into(), json!({ "type": "boolean" })); map.insert("idempotencyKey".into(), json!({ "type": "string" })); } json!({ "name": "mnote.index.update_settings", "description": "新增或删除本地索引范围;includePaths 为空表示删除当前用户索引范围并在无有效范围时清空索引文件。", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["index.write", "evidence.read"], "status": "available", "annotations": tool_annotations(false, true, false, false), "inputSchema": { "type": "object", "required": ["workspaceId", "rootUri", "includePaths", "dryRun", "idempotencyKey"], "properties": properties } }) } fn block_fetch_tool() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert("blockId".into(), json!({ "type": "string" })); map.insert( "includeChildren".into(), json!({ "type": "boolean", "default": true }), ); map.insert( "contextBefore".into(), json!({ "type": "integer", "default": 1 }), ); map.insert( "contextAfter".into(), json!({ "type": "integer", "default": 1 }), ); map.insert( "format".into(), json!({ "type": "string", "enum": ["json", "markdown", "text", "page_xml"], "default": "json" }), ); } json!({ "name": "mnote.block.fetch", "description": "读取单个块及同父级上下文", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["block.read", "page.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "required": ["workspaceId", "documentId", "sessionId", "runId", "toolCallId", "traceId", "actorId", "blockId"], "properties": properties } }) } fn doc_plan_update_tool() -> Value { write_tool( "mnote.doc.plan_update", "生成页面块更新 dry-run 计划,不直接写入", ["page.write", "block.write"], json!({ "command": { "type": "string" }, "blockId": { "type": "string" }, "anchorBlockId": { "type": "string" }, "content": { "type": ["string", "object", "array"] }, "query": { "type": "string" }, "revision": { "type": ["number", "string"] }, "conflictDetectionKey": { "type": "string" } }), [], ) } fn block_replace_tool() -> Value { write_tool( "mnote.block.replace", "兼容块写工具:替换指定块内容;本地 Markdown 普通编辑优先使用 agent 原生 patch/diff,必要时再走 Rust page.body.write 兼容链路", ["block.write", "page.write"], json!({ "blockId": { "type": "string" }, "content": { "type": ["string", "object", "array"] }, "revision": { "type": ["number", "string"] }, "conflictDetectionKey": { "type": "string" }, "blockRevisionRef": { "type": "string" }, "allowedTargetBlockIds": { "type": "array", "items": { "type": "string" } } }), [ "blockId", "content", "revision", "conflictDetectionKey", "blockRevisionRef", ], ) } fn block_insert_after_tool() -> Value { let mut tool = write_tool( "mnote.block.insert_after", "在指定块后插入新块;真实写入走 Rust page.body.save 链路", ["block.write", "page.write"], json!({ "anchorBlockId": { "type": "string" }, "content": { "type": ["string", "object", "array"] }, "block": { "type": "object" }, "blocks": { "type": "array", "minItems": 1, "maxItems": 20, "items": { "type": ["string", "object", "array"] } }, "revision": { "type": ["number", "string"] }, "conflictDetectionKey": { "type": "string" }, "anchorRevisionRef": { "type": "string" }, "allowedTargetBlockIds": { "type": "array", "items": { "type": "string" } } }), [ "anchorBlockId", "revision", "conflictDetectionKey", "anchorRevisionRef", ], ); if let Some(schema) = tool.get_mut("inputSchema").and_then(Value::as_object_mut) { schema.insert( "anyOf".into(), json!([ { "required": ["content"] }, { "required": ["block"] }, { "required": ["blocks"] } ]), ); } tool } fn block_move_after_tool() -> Value { write_tool( "mnote.block.move_after", "受限同父级普通叶子块移动;真实写入走 Rust page.body.save 链路", ["block.write", "page.write"], json!({ "blockId": { "type": "string" }, "anchorBlockId": { "type": "string" }, "revision": { "type": ["number", "string"] }, "conflictDetectionKey": { "type": "string" }, "blockRevisionRef": { "type": "string" }, "anchorRevisionRef": { "type": "string" }, "allowedTargetBlockIds": { "type": "array", "items": { "type": "string" } } }), [ "blockId", "anchorBlockId", "revision", "conflictDetectionKey", "blockRevisionRef", "anchorRevisionRef", ], ) } fn block_delete_tool() -> Value { write_tool( "mnote.block.delete", "删除指定无子块普通块;真实写入走 Rust page.body.save 链路", ["block.write", "page.write"], json!({ "blockId": { "type": "string" }, "revision": { "type": ["number", "string"] }, "conflictDetectionKey": { "type": "string" }, "blockRevisionRef": { "type": "string" }, "allowedTargetBlockIds": { "type": "array", "items": { "type": "string" } } }), [ "blockId", "revision", "conflictDetectionKey", "blockRevisionRef", ], ) } fn doc_apply_block_ops_tool() -> Value { write_tool( "mnote.doc.apply_block_ops", "一次性应用多个块级操作;Rust 侧统一读取最新 projection、生成 canonical content 并一次保存,适合页面 AI 小段落增删改移动快路径", ["block.write", "page.write"], json!({ "operations": { "type": "array", "items": { "type": "object", "properties": { "op": { "type": "string" }, "blockId": { "type": "string" }, "anchorBlockId": { "type": "string" }, "matchText": { "type": "string" }, "anchorText": { "type": "string" }, "afterText": { "type": "string" }, "allowedTargetBlockIds": { "type": "array", "items": { "type": "string" } }, "content": { "type": ["string", "object", "array"] } } } } }), ["operations"], ) } fn write_tool( name: &str, description: &str, scope: impl IntoIterator, extra_properties: Value, extra_required: impl IntoIterator, ) -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert("dryRun".into(), json!({ "type": "boolean" })); map.insert("idempotencyKey".into(), json!({ "type": "string" })); if let Value::Object(extra) = extra_properties { for (key, value) in extra { map.insert(key, value); } } } let mut required = vec![ "workspaceId", "documentId", "sessionId", "runId", "toolCallId", "traceId", "actorId", "dryRun", "idempotencyKey", ]; required.extend(extra_required); json!({ "name": name, "description": description, "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": scope.into_iter().collect::>(), "status": "available", "annotations": tool_annotations(false, false, false, false), "inputSchema": { "type": "object", "required": required, "properties": properties } }) } fn page_get_tool() -> Value { json!({ "name": "mnote.page.get", "description": "读取当前页面 Page Aggregate 摘要", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["page.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "required": ["workspaceId", "documentId", "sessionId", "runId", "toolCallId", "traceId", "actorId"], "properties": { "workspaceId": { "type": "string" }, "documentId": { "type": "string" }, "sessionId": { "type": "string" }, "runId": { "type": "string" }, "toolCallId": { "type": "string" }, "traceId": { "type": "string" }, "actorId": { "type": "string" }, "actorType": { "type": "string" }, "includeBody": { "type": "boolean", "default": true }, "includeOptions": { "type": "boolean", "default": true }, "includeBlocks": { "type": "boolean", "default": true } } } }) } fn page_save_tool() -> Value { json!({ "name": "mnote.page.save", "description": "粗粒度 compat / cloud 兜底:保存当前页面正文;local-first 本地 Markdown 普通编辑禁止使用该工具,必须优先让 agent 原生 patch/diff 直接编辑授权文件;仅在用户明确要求整页覆盖/追加且其它工具无法表达时使用", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["page.write"], "status": "available", "annotations": tool_annotations(false, true, false, false), "inputSchema": { "type": "object", "required": ["workspaceId", "documentId", "sessionId", "runId", "toolCallId", "traceId", "actorId", "content", "dryRun", "idempotencyKey"], "properties": { "workspaceId": { "type": "string" }, "documentId": { "type": "string" }, "sessionId": { "type": "string" }, "runId": { "type": "string" }, "toolCallId": { "type": "string" }, "traceId": { "type": "string" }, "actorId": { "type": "string" }, "actorType": { "type": "string" }, "content": { "description": "要写入的正文块数组、{blocks:[...]}、TipTap content 数组或纯文本", "type": ["array", "object", "string"] }, "mode": { "type": "string", "enum": ["replace", "append", "prepend"], "default": "replace" }, "dryRun": { "type": "boolean" }, "idempotencyKey": { "type": "string" } } } }) } fn available_tool( name: &str, description: &str, scope: impl IntoIterator, ) -> Value { json!({ "name": name, "description": description, "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": scope.into_iter().collect::>(), "status": "available", "annotations": tool_annotations(false, false, false, false) }) } fn doc_markdown_edit_tool() -> Value { let mut tool = write_tool( "mnote.doc.markdown_edit", "compat / remote / cloud fallback:通过文本级搜索替换编辑 markdown 内容。local-first 本地 workspace 的普通 Markdown 编辑禁止使用该工具,必须优先让 agent 原生 patch/diff 直接编辑授权文件;仅在远端代理、cloud/compat 或需要 MNote 结构校验时使用。", ["block.write", "page.write"], json!({ "operations": { "type": "array", "description": "搜索替换操作列表", "items": { "type": "object", "properties": { "search": { "type": "string", "description": "要搜索的原文片段" }, "replace": { "type": "string", "description": "替换后的新文本" } }, "required": ["search", "replace"] } }, "full_content": { "type": "string", "description": "完整修改后的 markdown 文本(替代 operations)" } }), [], ); if let Some(schema) = tool.get_mut("inputSchema").and_then(Value::as_object_mut) { schema.insert( "anyOf".into(), json!([ { "required": ["operations"] }, { "required": ["full_content"] } ]), ); } tool } fn resource_identity_properties(resource_id_name: &str) -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert("rootUri".into(), json!({ "type": "string" })); map.insert("sourceKind".into(), json!({ "type": "string" })); map.insert(resource_id_name.into(), json!({ "type": "string" })); map.insert("resourcePath".into(), json!({ "type": "string" })); map.insert( "aiAccessScope".into(), json!({ "type": "object", "properties": { "permissionLevel": { "type": "string" }, "allowedResourceIds": { "type": "array", "items": { "type": "string" } }, "allowedRoots": { "type": "array" } } }), ); } properties } fn mindmap_fetch_tool() -> Value { let mut properties = resource_identity_properties("mindmapId"); if let Value::Object(map) = &mut properties { map.insert( "scope".into(), json!({ "type": "string", "enum": ["tree", "subtree", "markdown_summary", "full_envelope"], "default": "tree" }), ); map.insert("nodeId".into(), json!({ "type": "string" })); } json!({ "name": "mnote.mindmap.fetch", "description": "读取授权 root 内的 mindmap resource 结构或 markdown summary", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["resource.read", "mindmap.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "required": ["workspaceId", "documentId", "mindmapId", "sessionId", "runId", "toolCallId", "traceId", "actorId"], "properties": properties } }) } fn mindmap_apply_ops_tool() -> Value { write_tool( "mnote.mindmap.apply_ops", "对授权 mindmap resource 生成或执行结构操作;本地 workspace 默认建议 agent 直接编辑授权文件,必要时才走该工具", ["resource.write", "mindmap.write"], json!({ "mindmapId": { "type": "string" }, "rootUri": { "type": "string" }, "sourceKind": { "type": "string" }, "resourcePath": { "type": "string" }, "expectedRevision": { "type": ["number", "string"] }, "ops": { "type": "array", "items": { "type": "object" } }, "aiAccessScope": { "type": "object" } }), ["mindmapId", "ops"], ) } fn mindmap_create_from_outline_tool() -> Value { write_tool( "mnote.mindmap.create_from_outline", "从 PDF、Office、Markdown 或页面摘要生成新的 MNote mindmap resource envelope", ["resource.write", "mindmap.write"], json!({ "mindmapId": { "type": "string" }, "rootUri": { "type": "string" }, "sourceKind": { "type": "string" }, "resourcePath": { "type": "string" }, "title": { "type": "string" }, "outline": { "type": "array", "items": { "type": "object" } }, "sourceRefs": { "type": "array", "items": { "type": "object" } }, "embedIntoPage": { "type": "boolean", "default": true }, "aiAccessScope": { "type": "object" } }), ["mindmapId", "outline"], ) } fn office_fetch_summary_tool() -> Value { let mut properties = resource_identity_properties("assetId"); if let Value::Object(map) = &mut properties { map.insert( "extractMode".into(), json!({ "type": "string", "enum": ["text", "outline", "metadata"], "default": "metadata" }), ); } json!({ "name": "mnote.office.fetch_summary", "description": "读取授权 Office resource 的元数据和轻量文本摘要;不直接写二进制文件", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["resource.read", "office.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "required": ["workspaceId", "documentId", "assetId", "sessionId", "runId", "toolCallId", "traceId", "actorId"], "properties": properties } }) } fn office_propose_changes_tool() -> Value { let mut properties = resource_identity_properties("assetId"); if let Value::Object(map) = &mut properties { map.insert("instructions".into(), json!({ "type": "string" })); map.insert( "basisRevision".into(), json!({ "type": ["number", "string"] }), ); } json!({ "name": "mnote.office.propose_changes", "description": "基于授权 Office resource 生成修改建议;真实写入仍由 OnlyOffice / officecli 完成", "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": ["resource.read", "office.read"], "status": "available", "annotations": tool_annotations(true, false, true, false), "inputSchema": { "type": "object", "required": ["workspaceId", "documentId", "assetId", "sessionId", "runId", "toolCallId", "traceId", "actorId", "instructions"], "properties": properties } }) } fn onlyoffice_base_properties() -> Value { let mut properties = base_identity_properties(); if let Value::Object(map) = &mut properties { map.insert("onlyofficeSessionId".into(), json!({ "type": "string" })); map.insert("bridgeSessionId".into(), json!({ "type": "string" })); map.insert( "aiAccessScope".into(), json!({ "type": "object", "required": ["allowedResourceIds"], "properties": { "permissionLevel": { "type": "string" }, "allowedResourceIds": { "type": "array", "items": { "type": "string" }, "minItems": 1 } } }), ); map.insert( "timeoutMs".into(), json!({ "type": "integer", "default": 25000 }), ); } properties } fn onlyoffice_session_current_tool() -> Value { onlyoffice_tool( "mnote.onlyoffice.session.current", "返回显式授权的 ONLYOFFICE live bridge session。", ["office.read"], true, onlyoffice_base_properties(), &["sessionId", "runId", "toolCallId", "traceId", "actorId"], ) } fn onlyoffice_capabilities_tool() -> Value { onlyoffice_tool( "mnote.onlyoffice.capabilities", "返回当前 MNote ONLYOFFICE bridge 支持的白名单 action 和编辑器能力矩阵。", ["office.read"], true, onlyoffice_base_properties(), &["sessionId", "runId", "toolCallId", "traceId", "actorId"], ) } fn onlyoffice_selection_get_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("options".into(), json!({ "type": "object" })); } onlyoffice_tool( "mnote.onlyoffice.selection.get", "读取当前 ONLYOFFICE 选区文本,支持文档、表格和演示。", ["office.read"], true, properties, &["sessionId", "runId", "toolCallId", "traceId", "actorId"], ) } fn onlyoffice_document_insert_text_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("text".into(), json!({ "type": "string" })); } onlyoffice_tool( "mnote.onlyoffice.document.insert_text", "向当前 ONLYOFFICE 文档/选区插入纯文本。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "text", ], ) } fn onlyoffice_document_replace_selection_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("text".into(), json!({ "type": "string" })); } onlyoffice_tool( "mnote.onlyoffice.document.replace_selection", "用纯文本替换当前 ONLYOFFICE 选区。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "text", ], ) } fn onlyoffice_document_insert_html_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("html".into(), json!({ "type": "string" })); } onlyoffice_tool( "mnote.onlyoffice.document.insert_html", "向当前 ONLYOFFICE 文档插入 HTML 片段。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "html", ], ) } fn onlyoffice_document_export_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert( "format".into(), json!({ "type": "string", "enum": ["markdown", "html"], "default": "markdown" }), ); } onlyoffice_tool( "mnote.onlyoffice.document.export", "把当前 ONLYOFFICE 文档导出为 Markdown 或 HTML 文本,适合 agent 读取全文后再提出修改。", ["office.read"], true, properties, &["sessionId", "runId", "toolCallId", "traceId", "actorId"], ) } fn onlyoffice_document_search_replace_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("search".into(), json!({ "type": "string" })); map.insert("replace".into(), json!({ "type": "string", "default": "" })); map.insert( "matchCase".into(), json!({ "type": "boolean", "default": false }), ); } onlyoffice_tool( "mnote.onlyoffice.document.search_replace", "在当前 ONLYOFFICE Word 文档中执行文本查找替换。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "search", ], ) } fn onlyoffice_document_insert_table_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("rows".into(), json!({ "type": "integer", "minimum": 1 })); map.insert("cols".into(), json!({ "type": "integer", "minimum": 1 })); map.insert("columns".into(), json!({ "type": "integer", "minimum": 1 })); map.insert( "data".into(), json!({ "type": "array", "items": { "type": "array", "items": {} } }), ); } onlyoffice_tool( "mnote.onlyoffice.document.insert_table", "向当前 ONLYOFFICE Word 文档末尾插入表格,可用二维 data 填充单元格。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", ], ) } fn onlyoffice_document_get_comments_tool() -> Value { onlyoffice_tool( "mnote.onlyoffice.document.get_comments", "读取当前 ONLYOFFICE Word 文档中的评论列表、作者、正文和引用文本。", ["office.read"], true, onlyoffice_base_properties(), &["sessionId", "runId", "toolCallId", "traceId", "actorId"], ) } fn onlyoffice_document_add_comment_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("text".into(), json!({ "type": "string" })); map.insert( "author".into(), json!({ "type": "string", "default": "MNote AI" }), ); map.insert( "target".into(), json!({ "type": "string", "enum": ["selection", "document_start"], "default": "selection" }), ); } onlyoffice_tool( "mnote.onlyoffice.document.add_comment", "给当前 ONLYOFFICE Word 文档选区或文档开头段落添加评论。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "text", ], ) } fn onlyoffice_sheet_get_sheets_tool() -> Value { onlyoffice_tool( "mnote.onlyoffice.sheet.get_sheets", "读取当前 ONLYOFFICE 表格工作簿的工作表列表。", ["office.read"], true, onlyoffice_base_properties(), &["sessionId", "runId", "toolCallId", "traceId", "actorId"], ) } fn onlyoffice_sheet_add_sheet_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("name".into(), json!({ "type": "string" })); } onlyoffice_tool( "mnote.onlyoffice.sheet.add_sheet", "在当前 ONLYOFFICE 表格工作簿中新增工作表,并切换为活动工作表。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "name", ], ) } fn onlyoffice_sheet_rename_sheet_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("name".into(), json!({ "type": "string" })); map.insert( "sheetIndex".into(), json!({ "type": "integer", "minimum": 0 }), ); map.insert("sheetName".into(), json!({ "type": "string" })); } onlyoffice_tool( "mnote.onlyoffice.sheet.rename_sheet", "重命名当前或指定 ONLYOFFICE 表格工作表。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "name", ], ) } fn onlyoffice_sheet_get_range_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("address".into(), json!({ "type": "string" })); } onlyoffice_tool( "mnote.onlyoffice.sheet.get_range", "读取当前 ONLYOFFICE 表格的指定单元格或区域;address 为空时读取当前选区。", ["office.read"], true, properties, &["sessionId", "runId", "toolCallId", "traceId", "actorId"], ) } fn onlyoffice_sheet_get_range_values_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("address".into(), json!({ "type": "string" })); map.insert("range".into(), json!({ "type": "string" })); } onlyoffice_tool( "mnote.onlyoffice.sheet.get_range_values", "按 A1 地址读取当前 ONLYOFFICE 表格 range 的二维值、显示值和公式。", ["office.read"], true, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "address", ], ) } fn onlyoffice_sheet_get_values_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert( "startRow".into(), json!({ "type": "integer", "default": 0 }), ); map.insert( "startCol".into(), json!({ "type": "integer", "default": 0 }), ); map.insert( "rowCount".into(), json!({ "type": "integer", "minimum": 1 }), ); map.insert( "colCount".into(), json!({ "type": "integer", "minimum": 1 }), ); } onlyoffice_tool( "mnote.onlyoffice.sheet.get_values", "按零基行列坐标批量读取当前 ONLYOFFICE 表格单元格值、显示值和公式。", ["office.read"], true, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "rowCount", "colCount", ], ) } fn onlyoffice_sheet_set_value_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("address".into(), json!({ "type": "string" })); map.insert("value".into(), json!({})); } onlyoffice_tool( "mnote.onlyoffice.sheet.set_value", "写入当前 ONLYOFFICE 表格的指定单元格或区域;address 为空时写入当前选区。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "value", ], ) } fn onlyoffice_sheet_set_formula_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("address".into(), json!({ "type": "string" })); map.insert("formula".into(), json!({ "type": "string" })); } onlyoffice_tool( "mnote.onlyoffice.sheet.set_formula", "向当前 ONLYOFFICE 表格写入公式。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "formula", ], ) } fn onlyoffice_sheet_batch_set_values_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert( "startRow".into(), json!({ "type": "integer", "default": 0 }), ); map.insert( "startCol".into(), json!({ "type": "integer", "default": 0 }), ); map.insert( "values".into(), json!({ "type": "array", "items": { "type": "array", "items": {} } }), ); } onlyoffice_tool( "mnote.onlyoffice.sheet.batch_set_values", "按零基行列坐标向当前 ONLYOFFICE 表格批量写入二维数组。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "values", ], ) } fn onlyoffice_sheet_set_range_values_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("address".into(), json!({ "type": "string" })); map.insert("range".into(), json!({ "type": "string" })); map.insert( "values".into(), json!({ "type": "array", "items": { "type": "array", "items": {} } }), ); } onlyoffice_tool( "mnote.onlyoffice.sheet.set_range_values", "按 A1 地址向当前 ONLYOFFICE 表格 range 写入二维数组。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "address", "values", ], ) } fn onlyoffice_sheet_format_range_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("address".into(), json!({ "type": "string" })); map.insert("range".into(), json!({ "type": "string" })); map.insert("bold".into(), json!({ "type": "boolean" })); map.insert("italic".into(), json!({ "type": "boolean" })); map.insert("underline".into(), json!({ "type": "boolean" })); map.insert("fillColor".into(), json!({ "type": "string" })); map.insert("fontColor".into(), json!({ "type": "string" })); map.insert("fontSize".into(), json!({ "type": "number", "minimum": 0 })); map.insert("fontName".into(), json!({ "type": "string" })); map.insert( "horizontalAlign".into(), json!({ "type": "string", "enum": ["left", "center", "right", "justify"] }), ); map.insert( "verticalAlign".into(), json!({ "type": "string", "enum": ["top", "center", "bottom"] }), ); map.insert("numberFormat".into(), json!({ "type": "string" })); } onlyoffice_tool( "mnote.onlyoffice.sheet.format_range", "按 A1 地址设置当前 ONLYOFFICE 表格 range 的字体、颜色、对齐和数字格式。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "address", ], ) } fn onlyoffice_sheet_set_dimensions_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert( "columnIndex".into(), json!({ "type": "integer", "minimum": 0 }), ); map.insert( "columnWidth".into(), json!({ "type": "number", "minimum": 0 }), ); map.insert( "rowIndex".into(), json!({ "type": "integer", "minimum": 0 }), ); map.insert( "rowHeight".into(), json!({ "type": "number", "minimum": 0 }), ); } onlyoffice_tool( "mnote.onlyoffice.sheet.set_dimensions", "设置当前 ONLYOFFICE 表格的列宽或行高;行列索引为零基。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", ], ) } fn onlyoffice_sheet_sort_range_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("address".into(), json!({ "type": "string" })); map.insert("range".into(), json!({ "type": "string" })); map.insert( "keyColumn".into(), json!({ "type": "integer", "minimum": 1, "default": 1 }), ); map.insert( "order".into(), json!({ "type": "string", "enum": ["ascending", "descending", "asc", "desc"], "default": "ascending" }), ); map.insert( "header".into(), json!({ "type": "boolean", "default": true }), ); } onlyoffice_tool( "mnote.onlyoffice.sheet.sort_range", "按 A1 range 对当前 ONLYOFFICE 表格区域排序;keyColumn 为 range 内从 1 开始的排序列。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "address", ], ) } fn onlyoffice_sheet_add_chart_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("address".into(), json!({ "type": "string" })); map.insert("range".into(), json!({ "type": "string" })); map.insert( "chartType".into(), json!({ "type": "string", "default": "bar" }), ); map.insert( "inColumns".into(), json!({ "type": "boolean", "default": true }), ); map.insert("style".into(), json!({ "type": "integer", "default": 2 })); map.insert( "widthMm".into(), json!({ "type": "number", "minimum": 0, "default": 120 }), ); map.insert( "heightMm".into(), json!({ "type": "number", "minimum": 0, "default": 80 }), ); map.insert( "fromCol".into(), json!({ "type": "integer", "minimum": 0, "default": 4 }), ); map.insert( "fromRow".into(), json!({ "type": "integer", "minimum": 0, "default": 1 }), ); map.insert( "xOffsetMm".into(), json!({ "type": "number", "minimum": 0, "default": 0 }), ); map.insert( "yOffsetMm".into(), json!({ "type": "number", "minimum": 0, "default": 0 }), ); } onlyoffice_tool( "mnote.onlyoffice.sheet.add_chart", "基于当前 ONLYOFFICE 表格 A1 range 插入图表,默认生成柱状图。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "address", ], ) } fn onlyoffice_presentation_get_slides_tool() -> Value { onlyoffice_tool( "mnote.onlyoffice.presentation.get_slides", "读取当前 ONLYOFFICE 演示文稿的基础幻灯片信息。", ["office.read"], true, onlyoffice_base_properties(), &["sessionId", "runId", "toolCallId", "traceId", "actorId"], ) } fn onlyoffice_presentation_get_slide_texts_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert( "slideIndex".into(), json!({ "type": "integer", "minimum": 0 }), ); } onlyoffice_tool( "mnote.onlyoffice.presentation.get_slide_texts", "读取当前 ONLYOFFICE 演示文稿中每页文本框的纯文本。", ["office.read"], true, properties, &["sessionId", "runId", "toolCallId", "traceId", "actorId"], ) } fn onlyoffice_presentation_get_shapes_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert( "slideIndex".into(), json!({ "type": "integer", "minimum": 0 }), ); } onlyoffice_tool( "mnote.onlyoffice.presentation.get_shapes", "读取当前 ONLYOFFICE 演示文稿的 shape 列表、shapeId、shapeIndex 和文本。", ["office.read"], true, properties, &["sessionId", "runId", "toolCallId", "traceId", "actorId"], ) } fn onlyoffice_presentation_add_text_slide_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("title".into(), json!({ "type": "string" })); map.insert("body".into(), json!({ "type": "string" })); } onlyoffice_tool( "mnote.onlyoffice.presentation.add_text_slide", "向当前 ONLYOFFICE 演示文稿追加一个文本幻灯片。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", ], ) } fn onlyoffice_presentation_replace_text_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert("search".into(), json!({ "type": "string" })); map.insert("replace".into(), json!({ "type": "string", "default": "" })); map.insert( "slideIndex".into(), json!({ "type": "integer", "minimum": 0 }), ); map.insert( "matchCase".into(), json!({ "type": "boolean", "default": false }), ); map.insert( "replaceAll".into(), json!({ "type": "boolean", "default": true }), ); } onlyoffice_tool( "mnote.onlyoffice.presentation.replace_text", "替换当前 ONLYOFFICE 演示文稿文本框中的纯文本;命中的文本框会按纯文本段落重建。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "search", ], ) } fn onlyoffice_presentation_set_shape_text_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert( "slideIndex".into(), json!({ "type": "integer", "minimum": 0 }), ); map.insert( "shapeIndex".into(), json!({ "type": "integer", "minimum": 0 }), ); map.insert("shapeId".into(), json!({ "type": "string" })); map.insert("text".into(), json!({ "type": "string" })); } onlyoffice_tool( "mnote.onlyoffice.presentation.set_shape_text", "用 slideIndex + shapeIndex/shapeId 精确设置 PPT 文本 shape 的纯文本。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "slideIndex", "text", ], ) } fn onlyoffice_presentation_delete_slide_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert( "slideIndex".into(), json!({ "type": "integer", "minimum": 0 }), ); } onlyoffice_tool( "mnote.onlyoffice.presentation.delete_slide", "删除当前 ONLYOFFICE 演示文稿中的指定幻灯片。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "slideIndex", ], ) } fn onlyoffice_presentation_add_table_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert( "slideIndex".into(), json!({ "type": "integer", "minimum": 0 }), ); map.insert("rows".into(), json!({ "type": "integer", "minimum": 1 })); map.insert("cols".into(), json!({ "type": "integer", "minimum": 1 })); map.insert("columns".into(), json!({ "type": "integer", "minimum": 1 })); map.insert( "data".into(), json!({ "type": "array", "items": { "type": "array", "items": {} } }), ); map.insert("xMm".into(), json!({ "type": "number", "minimum": 0 })); map.insert("yMm".into(), json!({ "type": "number", "minimum": 0 })); map.insert( "widthMm".into(), json!({ "type": "number", "minimum": 0, "default": 190 }), ); map.insert( "heightMm".into(), json!({ "type": "number", "minimum": 0, "default": 90 }), ); } onlyoffice_tool( "mnote.onlyoffice.presentation.add_table", "向当前 ONLYOFFICE PPT 指定幻灯片插入表格,可用二维 data 填充单元格。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", ], ) } fn onlyoffice_presentation_clear_slide_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert( "slideIndex".into(), json!({ "type": "integer", "minimum": 0 }), ); } onlyoffice_tool( "mnote.onlyoffice.presentation.clear_slide", "清空当前 ONLYOFFICE PPT 指定幻灯片上的所有对象。调用前必须确认 slideIndex。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", "slideIndex", ], ) } fn onlyoffice_presentation_add_shape_tool() -> Value { let mut properties = onlyoffice_base_properties(); if let Value::Object(map) = &mut properties { map.insert( "slideIndex".into(), json!({ "type": "integer", "minimum": 0 }), ); map.insert( "shapeType".into(), json!({ "type": "string", "enum": ["rect", "roundRect", "ellipse", "triangle", "diamond", "cube", "cloud", "flowChartMagneticTape"], "default": "rect" }), ); map.insert("text".into(), json!({ "type": "string" })); map.insert( "fillColor".into(), json!({ "type": "string", "default": "#4F81BD" }), ); map.insert("strokeColor".into(), json!({ "type": "string" })); map.insert( "strokeWidthMm".into(), json!({ "type": "number", "minimum": 0, "default": 0 }), ); map.insert( "xMm".into(), json!({ "type": "number", "minimum": 0, "default": 20 }), ); map.insert( "yMm".into(), json!({ "type": "number", "minimum": 0, "default": 35 }), ); map.insert( "widthMm".into(), json!({ "type": "number", "minimum": 0, "default": 160 }), ); map.insert( "heightMm".into(), json!({ "type": "number", "minimum": 0, "default": 70 }), ); } onlyoffice_tool( "mnote.onlyoffice.presentation.add_shape", "向当前 ONLYOFFICE PPT 指定幻灯片插入基础形状,可设置文字、填充色、位置和尺寸。", ["office.write"], false, properties, &[ "sessionId", "runId", "toolCallId", "traceId", "actorId", "idempotencyKey", "dryRun", ], ) } fn onlyoffice_tool( name: &'static str, description: &'static str, capability_scope: impl IntoIterator, readonly: bool, properties: Value, required: &[&'static str], ) -> Value { let mut required_fields = required.to_vec(); if name != "mnote.onlyoffice.capabilities" { required_fields.push("aiAccessScope"); } let mut input_schema = json!({ "type": "object", "required": required_fields, "properties": properties }); if name != "mnote.onlyoffice.capabilities" { input_schema["anyOf"] = json!([ { "required": ["onlyofficeSessionId"] }, { "required": ["bridgeSessionId"] } ]); } json!({ "name": name, "description": description, "schemaVersion": TOOL_SCHEMA_VERSION, "capabilityScope": capability_scope.into_iter().collect::>(), "status": "available", "annotations": tool_annotations(readonly, false, readonly, false), "inputSchema": input_schema }) } fn tool_annotations( readonly: bool, destructive: bool, idempotent: bool, requires_approval: bool, ) -> Value { json!({ "readonly": readonly, "destructive": destructive, "idempotent": idempotent, "readOnly": readonly, "requiresWritePermission": !readonly, "requiresApproval": requires_approval, "approvalMode": if requires_approval { "review" } else { "yolo" }, "runtimeOwner": "mnote-web", "writeOwner": "rust-runtime-kernel", "selectionEffect": if readonly { "preserve" } else { "may_change" } }) }