Files
mnote/rust/crates/mnote-web/src/hermes_tools/manifest.rs
T

2241 lines
75 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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(assemble_all_tools());
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 assemble_all_tools() -> Vec<Value> {
let mut tools = Vec::new();
tools.push(skill_read_tool());
tools.append(&mut context_tools());
tools.append(&mut doc_tools());
tools.append(&mut knowledge_rag_tools());
tools.append(&mut block_tools());
tools.append(&mut page_tools());
tools.append(&mut mindmap_tools());
tools.append(&mut office_tools());
tools.append(&mut onlyoffice_tools());
tools.append(&mut artifact_tools());
tools
}
fn context_tools() -> Vec<Value> {
vec![
context_snapshot_tool(),
context_read_current_page_tool(),
context_resolve_target_tool(),
]
}
fn doc_tools() -> Vec<Value> {
vec![
doc_fetch_tool(),
doc_find_tool(),
doc_markdown_edit_tool(),
doc_apply_block_ops_tool(),
doc_plan_update_tool(),
]
}
fn knowledge_rag_tools() -> Vec<Value> {
vec![
knowledge_rag_status_tool(),
weknora_search_tool(),
weknora_list_sources_tool(),
weknora_get_source_status_tool(),
weknora_open_reference_tool(),
knowledge_rag_query_tool(),
knowledge_rag_section_context_tool(),
knowledge_rag_open_reference_tool(),
]
}
fn block_tools() -> Vec<Value> {
vec![
block_fetch_tool(),
block_replace_tool(),
block_insert_after_tool(),
block_delete_tool(),
block_move_after_tool(),
]
}
fn page_tools() -> Vec<Value> {
vec![
page_get_tool(),
page_save_tool(),
available_tool(
"mnote.page.update_title",
"更新当前页面标题",
["page.write"],
),
available_tool(
"mnote.page.update_options",
"更新当前页面设置",
["page.write"],
),
]
}
fn mindmap_tools() -> Vec<Value> {
vec![
mindmap_fetch_tool(),
mindmap_apply_ops_tool(),
mindmap_create_from_outline_tool(),
]
}
fn office_tools() -> Vec<Value> {
vec![office_fetch_summary_tool(), office_propose_changes_tool()]
}
fn onlyoffice_tools() -> Vec<Value> {
vec![
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(),
]
}
fn artifact_tools() -> Vec<Value> {
vec![
available_tool(
"mnote.artifact.create_summary",
"为当前页面创建或更新 AI Summary",
["artifact.write"],
),
available_tool(
"mnote.artifact.create_ai_note",
"基于当前页面创建新的 AI Note",
["artifact.write"],
),
]
}
fn annotate_tools_with_capabilities(tools: Vec<Value>) -> Vec<Value> {
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
}
})
}
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": "查看当前资料库 provider 状态、dashboard 地址、source registry 和同步状态。默认 provider 是 WeKnoraLightRAG 仅作为 legacy fallback。",
"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 weknora_scope_properties() -> Value {
let mut properties = base_identity_properties();
if let Value::Object(map) = &mut properties {
map.insert("rootUri".into(), json!({ "type": "string" }));
map.insert("scope".into(), json!({ "type": "object" }));
map.insert(
"allowlist".into(),
json!({ "type": "array", "items": { "type": "string" } }),
);
map.insert("allowedRoots".into(), json!({ "type": "array" }));
map.insert("aiAccessScope".into(), json!({ "type": "object" }));
map.insert(
"sourcePaths".into(),
json!({ "type": "array", "items": { "type": "string" } }),
);
}
properties
}
fn weknora_search_tool() -> Value {
let mut properties = weknora_scope_properties();
if let Value::Object(map) = &mut properties {
map.insert("query".into(), json!({ "type": "string" }));
map.insert("topK".into(), json!({ "type": "integer", "default": 10 }));
map.insert(
"includeChunkContent".into(),
json!({ "type": "boolean", "default": true }),
);
}
json!({
"name": "mnote.weknora.search",
"description": "只读调用 WeKnora 检索。必须带 rootUri 和 scope/allowlist,返回 provider ids、MNote source registry 映射以及 locatorDegraded;不要把 WeKnora filename/chunk id 当成本地 path。",
"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 weknora_list_sources_tool() -> Value {
json!({
"name": "mnote.weknora.list_sources",
"description": "只读列出 MNote source registry 与 WeKnora provider 状态。必须带 rootUri 和 scope/allowlist。",
"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"],
"properties": weknora_scope_properties()
}
})
}
fn weknora_get_source_status_tool() -> Value {
let mut properties = weknora_scope_properties();
if let Value::Object(map) = &mut properties {
map.insert("sourcePath".into(), json!({ "type": "string" }));
map.insert("providerKnowledgeId".into(), json!({ "type": "string" }));
map.insert("providerSourceId".into(), json!({ "type": "string" }));
}
json!({
"name": "mnote.weknora.get_source_status",
"description": "只读查询某个 MNote source 或 WeKnora provider knowledge id 的映射状态。必须带 rootUri 和 scope/allowlist。",
"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"],
"properties": properties
}
})
}
fn weknora_open_reference_tool() -> Value {
let mut properties = weknora_scope_properties();
if let Value::Object(map) = &mut properties {
map.insert("reference".into(), json!({ "type": "object" }));
map.insert(
"providerKnowledgeBaseId".into(),
json!({ "type": "string" }),
);
map.insert("providerKnowledgeId".into(), json!({ "type": "string" }));
map.insert("providerChunkId".into(), json!({ "type": "string" }));
}
json!({
"name": "mnote.weknora.open_reference",
"description": "只读把 WeKnora reference 映射为 MNote open action。provider ids 独立返回;未命中 source registry 时必须视为 locatorDegraded。",
"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", "reference"],
"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(
"includeDocumentStructureIndex".into(),
json!({
"type": "boolean",
"default": false,
"description": "返回由 provider sidecar/headings 派生的 document_structure_index;适合大书/长文档问题做章节导航和上下文扩展,不替代 references 引用证据。"
}),
);
map.insert(
"sourcePaths".into(),
json!({
"type": "array",
"items": { "type": "string" },
"description": "可选 MNote workspace 相对路径范围;可传文件或目录。当前语义是 provider 检索后,MNote 只过滤返回的 referencesprovider raw 仍可能是全局结果。"
}),
);
}
json!({
"name": "mnote.knowledge_rag.query",
"description": "向当前资料库 provider 提问,返回 provider 原始结果和经 MNote registry 映射、sourcePaths 后过滤的 references/citations。默认 provider 是 WeKnoraLightRAG 仅 legacy fallback。用户要求链接、来源、引用或证据时优先调用;返回的 citationMarkdown 由 MNote 前端自动追加成可点击来源定位,agent 不应在最终回答中手写 /documents、mnote:// 或搜索引擎包装链接。",
"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": "把 provider 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
}
})
}
fn knowledge_rag_section_context_tool() -> Value {
let mut properties = base_identity_properties();
if let Value::Object(map) = &mut properties {
map.insert("rootUri".into(), json!({ "type": "string" }));
map.insert("sourcePath".into(), json!({ "type": "string" }));
map.insert("sourceId".into(), json!({ "type": "string" }));
map.insert("lightRagDocId".into(), json!({ "type": "string" }));
map.insert("filePath".into(), json!({ "type": "string" }));
map.insert("sectionId".into(), json!({ "type": "string" }));
map.insert("startBlockOrdinal".into(), json!({ "type": "integer" }));
map.insert("endBlockOrdinal".into(), json!({ "type": "integer" }));
map.insert("startParagraphOrdinal".into(), json!({ "type": "integer" }));
map.insert("endParagraphOrdinal".into(), json!({ "type": "integer" }));
map.insert(
"contextBefore".into(),
json!({ "type": "integer", "default": 1 }),
);
map.insert(
"contextAfter".into(),
json!({ "type": "integer", "default": 1 }),
);
map.insert(
"maxBlocks".into(),
json!({ "type": "integer", "default": 24 }),
);
map.insert(
"maxChars".into(),
json!({ "type": "integer", "default": 12000 }),
);
}
json!({
"name": "mnote.knowledge_rag.section_context",
"description": "按 documentStructureIndex section 的 block/paragraph range,从 provider sidecar 拉取有限正文 blocks/chunks,供大书/长文档二次解读;不做检索、重排或 fallback。",
"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"],
"properties": properties
}
})
}
// 旧 local index agent 工具仅保留为历史对照;当前 manifest() 不注册这些工具。
#[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<Item = &'static str>,
extra_properties: Value,
extra_required: impl IntoIterator<Item = &'static str>,
) -> 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::<Vec<_>>(),
"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<Item = &'static str>,
) -> Value {
json!({
"name": name,
"description": description,
"schemaVersion": TOOL_SCHEMA_VERSION,
"capabilityScope": scope.into_iter().collect::<Vec<_>>(),
"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<Item = &'static str>,
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::<Vec<_>>(),
"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" }
})
}