forked from 1b-agent/opencadstudio-upstream
fix(mcp): omit image payloads for unchanged diff captures
Return metadata only when a diff capture detects no visual change, avoiding repeated image payloads across all delivery modes
This commit is contained in:
1 parent
44ffb1ca5a
commit
d3274543b7
2 files changed
+66
No files matched your search
@@ -227,6 +227,14 @@ unconsumed, and the command closed rather than left armed for a second line. Geo
|
||||
|
||||
`ocs_capture` defaults to the drawing viewport and a longest edge of 1600 pixels. Set `scope` to `window` for the full interface or change `max_dimension` between 256 and 4096.
|
||||
|
||||
With `diff: true`, an unchanged comparison returns metadata only, including
|
||||
`diff.changed: false`, the hash and resource URI; it does not resend image or
|
||||
resource content blocks, regardless of `delivery`. The initial baseline (also
|
||||
after `reset_diff_baseline: true`) still uses the requested image delivery.
|
||||
Changed captures retain the requested diff mode, including cropped patches.
|
||||
Use `diff: false` or read the returned resource URI to retrieve the full image
|
||||
when an unchanged comparison omitted it.
|
||||
|
||||
Clients using MCP 2026-07-28 can advertise `io.modelcontextprotocol/tasks`. OCS then returns a standard task handle when an operation is still running and accepts `tasks/get`, `tasks/update`, and `tasks/cancel`. Other clients continue to receive the existing operation status and can read it with `ocs_read`.
|
||||
|
||||
The source-tree protocol smoke test treats the executable as a black box and uses only the Python standard library. It checks both supported protocol styles, the published schemas, structured errors, and the four-tool surface:
|
||||
|
||||
+58
@@ -2311,6 +2311,16 @@ pub(crate) fn tool_definitions() -> Value {
|
||||
|
||||
fn tool_result(value: Value) -> Value {
|
||||
if value.get("$resource").is_some() || value.get("$image").is_some() {
|
||||
// A baseline also reports changed=false, but the client has not seen
|
||||
// that image yet. Only omit media after an actual unchanged comparison.
|
||||
// Keep the capture metadata (hash, URI, spatial data and request id)
|
||||
// so clients can still fetch the stored image explicitly if needed.
|
||||
let meta = &value["metadata"];
|
||||
if meta["diff"]["is_baseline"].as_bool() == Some(false)
|
||||
&& meta["diff"]["changed"].as_bool() == Some(false)
|
||||
{
|
||||
return tool_result(meta.clone());
|
||||
}
|
||||
let mut content = Vec::new();
|
||||
if let Some(res) = value.get("$resource") {
|
||||
let uri = res["uri"].as_str().unwrap_or("");
|
||||
@@ -3778,6 +3788,54 @@ mod tests {
|
||||
assert!(both_content.iter().any(|c| c["type"] == "image"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unchanged_capture_omits_media_but_preserves_metadata() {
|
||||
let image = image::RgbaImage::from_pixel(4, 4, image::Rgba([255, 255, 255, 255]));
|
||||
let diff = crate::app::control::vision::compute_visual_diff(
|
||||
&image, &image, &[1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
|
||||
);
|
||||
assert!(!diff.changed);
|
||||
for delivery in ["inline", "resource", "both"] {
|
||||
let metadata = json!({
|
||||
"hash":"unchanged-hash", "uri":"cad://session/s/snapshot/unchanged-hash.png",
|
||||
"request_id":"capture-2", "revision":12,
|
||||
"_spatial":{"crs":"CAD_WCS"},
|
||||
"diff":{"is_baseline":false,"changed":diff.changed}
|
||||
});
|
||||
let mut capture = json!({"metadata":metadata});
|
||||
if delivery != "resource" {
|
||||
capture["$image"] = json!("image-bytes-must-not-be-returned");
|
||||
}
|
||||
if delivery != "inline" {
|
||||
capture["$resource"] = json!({"uri":metadata["uri"],"mimeType":"image/png"});
|
||||
}
|
||||
let result = tool_result(capture);
|
||||
assert_eq!(result["isError"], false);
|
||||
assert_eq!(result["structuredContent"], metadata);
|
||||
let content = result["content"].as_array().unwrap();
|
||||
assert_eq!(content.len(), 1);
|
||||
assert_eq!(content[0]["type"], "text");
|
||||
assert_eq!(serde_json::from_str::<Value>(content[0]["text"].as_str().unwrap()).unwrap(), metadata);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn baseline_changed_and_non_diff_captures_keep_images() {
|
||||
for diff in [
|
||||
json!({"is_baseline":true,"changed":false}),
|
||||
json!({"is_baseline":false,"changed":true}),
|
||||
Value::Null,
|
||||
] {
|
||||
let result = tool_result(json!({
|
||||
"$image":"baseline-or-changed-png",
|
||||
"metadata":{"diff":diff}
|
||||
}));
|
||||
assert!(result["content"].as_array().unwrap().iter().any(|block|
|
||||
block["type"] == "image" && block["data"] == "baseline-or-changed-png"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn capture_tool_schema_and_diff_metadata_handling() {
|
||||
// 1. Verify ocs_capture schema has diff parameters
|
||||
|
||||
Reference in new issue
Block a user