動作
每個視窗層級的動作都會變成一個名為 {winId}__{action} 的 MCP 工具。用 win.actions().add(name, title) 建立它,逐步調整,最後以 .on(handler) 收尾。
建構器選項
| 方法 | 效果 |
|---|---|
.description(s) | 在 tools/list 中提供人類可讀的描述 |
.params::<T>() | 由 T: JsonSchema + DeserializeOwned 產生 inputSchema |
.returns::<R>() | 貢獻 outputSchema 中的 result 子結構描述 |
.read_only() .destructive() .idempotent() .open_world() | MCP 註記提示 |
.available(bool) | 在 tools/list 中的初始可見性 |
處理器形式
.on(..) 接受六種形態。用兩個問題來選擇:它需要參數嗎? 以及 它需要宿主環境 &mut Cx 嗎?
| 形式 | 執行於 | 適用於 |
|---|---|---|
on_ui(|| …) | 主執行緒 | 純 UI 邏輯,無參數 |
on_ui(|p| …) | 主執行緒 | 帶參數的純 UI 邏輯 |
on_ui(|cx| …) | 主執行緒 | 需要操作真正的元件,無參數 |
on_ui(|p, cx| …) | 主執行緒 | 需要操作真正的元件,帶參數 |
|| async { … } | tokio | 非同步工作,無參數 |
|p| async { … } | tokio | 帶參數的非同步工作 |
rust
#[derive(Deserialize, JsonSchema)]
struct Add { text: String }
#[derive(Serialize, JsonSchema)]
struct Stats { total: usize, done: usize }
win.actions()
.add("add", "Add a todo")
.description("Append a new open item.")
.params::<Add>() // → inputSchema
.returns::<Stats>() // → outputSchema
.on(on_ui(move |p: Add, _cx: &mut Cx| {
let text = p.text.trim().to_string();
if text.is_empty() {
return Err(ActionError::field("text", "must not be empty"));
}
// …mutate real state, then let the read side mirror it…
Ok(Outcome::message("added").with(Stats { total: 1, done: 0 }))
}));回傳結果
可以回傳 &str、String 或 Outcome。Outcome::message(..).with(value) 會附帶結構化資料,讓 Agent 直接解析,而不必重新讀取整份快照。
非同步處理器與主執行緒
Important
async 處理器在 tokio 上執行,因此不會收到 &mut Cx。若要在 await 之後操作 UI,請擷取一個 Ui<Cx>(來自 app.ui())並跳回主執行緒:
rust
let ui = app.ui();
win.actions().add("report", "Build a report").on(move || {
let ui = ui.clone();
async move {
let data = fetch_from_network().await; // off-thread work
let n = ui.run(move |cx| apply(cx, data)).await; // back on the main thread
Ok::<_, ActionError>(Outcome::message(format!("{n} rows")))
}
});在執行期改變動作
.on(..) 會回傳一個 ActionHandle。保留它,就能隱藏當下不該被 Agent 呼叫的工具——工具清單會重新計算,並通知客戶端:
rust
let clear = win.actions().add("clear_done", "Clear completed")
.available(false) // hidden until useful
.on(on_ui(|| Ok::<_, ActionError>("cleared")));
// In sync(), as state changes:
clear.set_available(self.items.iter().any(|t| t.done));ActionHandle 另外還有 set_title、set_description 和 remove。
為何值得這樣做
一個只會看到真正可呼叫的工具的 Agent,不必去猜測,也不會把一輪對話浪費在註定失敗的動作上。