Merge PR #2152: MCP: mark file-writing tools as potentially destructive

This commit is contained in:
aiongg committed 2026-10-21 14:48:49 +00:00
commit 145095663b
3 files changed
+33 -4

No files matched your search

+1 -2
View File
@@ -384,8 +384,7 @@ fn annotations(name: &str, title: &str) -> Value {
| "ui_inspect"
| "ui_screenshot"
);
let files = matches!(name, "export" | "render_photo" | "screenshot");
json!({"title": title, "readOnlyHint": read_only, "destructiveHint": !read_only && !files,
json!({"title": title, "readOnlyHint": read_only, "destructiveHint": !read_only,
"idempotentHint": read_only || matches!(name, "render_photo" | "screenshot"), "openWorldHint": false})
}
+27
View File
@@ -78,6 +78,33 @@ fn exposure_roundtrip(server: &mut Server) {
assert!(darker < before - 15.0, "exposure -1.5 should darken: {before} → {darker}");
}
#[test]
fn file_writers_advertise_destructive_effects() {
for with_commands in [false, true] {
let mut server = Server::new(Box::new(Headless::demo())).with_command_tools(with_commands);
let replies = session(&mut server, &[json!({"jsonrpc": "2.0", "id": 1, "method": "tools/list"})]);
let tools = replies[0]["result"]["tools"].as_array().unwrap();
for name in ["export", "render_photo"] {
let tool = tools.iter().find(|t| t["name"] == name).unwrap();
assert_eq!(tool["annotations"]["readOnlyHint"], false, "{name}");
assert_eq!(tool["annotations"]["destructiveHint"], true, "{name} can replace a file");
}
let preview = tools.iter().find(|t| t["name"] == "render_preview").unwrap();
assert_eq!(preview["annotations"]["readOnlyHint"], true);
assert_eq!(preview["annotations"]["destructiveHint"], false);
}
let addr = fake_app();
let mut server = Server::new(Box::new(Remote::connect(&addr).unwrap()));
let replies = session(&mut server, &[json!({"jsonrpc": "2.0", "id": 1, "method": "tools/list"})]);
let tools = replies[0]["result"]["tools"].as_array().unwrap();
let screenshot = tools.iter().find(|t| t["name"] == "screenshot").unwrap();
assert_eq!(screenshot["annotations"]["readOnlyHint"], false);
assert_eq!(screenshot["annotations"]["destructiveHint"], true);
let image_only = tools.iter().find(|t| t["name"] == "ui_screenshot").unwrap();
assert_eq!(image_only["annotations"]["readOnlyHint"], true);
assert_eq!(image_only["annotations"]["destructiveHint"], false);
}
#[test]
fn headless_set_exposure_and_render() {
exposure_roundtrip(&mut Server::new(Box::new(Headless::demo())));