diff --git a/docs/content/docs/reference/cua-driver/mcp-tools.mdx b/docs/content/docs/reference/cua-driver/mcp-tools.mdx index 7ab5babf10..689c041bda 100644 --- a/docs/content/docs/reference/cua-driver/mcp-tools.mdx +++ b/docs/content/docs/reference/cua-driver/mcp-tools.mdx @@ -524,6 +524,8 @@ Caveats: Update cua-driver-rs configuration. Changes to `capture_mode` and `max_image_dimension` take effect immediately. The `experimental_pip` keys are persisted to `~/.cua-driver/config.json` and take effect on the next daemon restart. +Two equivalent argument shapes are accepted on every platform: a **direct field** (`{"capture_scope": "desktop"}`) or a **`{key, value}` pair** (`{"key": "capture_scope", "value": "desktop"}`, the same shape the CLI `config set` uses). A direct field wins if both are supplied. + **Per-session isolation (daemon-proxy path).** On the daemon-proxy path, `set_config` writes an in-memory, session-scoped override that does not touch the global `DriverConfig` or persist to disk. `get_config` and capture tools resolve effective values as: call-arg > session override > global default. The override is dropped automatically when the client disconnects. Only the anonymous path (`cua-driver config set` CLI, one-shot `cua-driver call`) writes the persisted global default. | Argument | Type | Required | Description | diff --git a/libs/cua-driver/rust/crates/platform-macos/src/tools/set_config.rs b/libs/cua-driver/rust/crates/platform-macos/src/tools/set_config.rs index d5c82420c5..ce251c7665 100644 --- a/libs/cua-driver/rust/crates/platform-macos/src/tools/set_config.rs +++ b/libs/cua-driver/rust/crates/platform-macos/src/tools/set_config.rs @@ -26,6 +26,15 @@ fn def() -> &'static ToolDef { input_schema: serde_json::json!({ "type": "object", "properties": { + "key": { + "type": "string", + "description": "Name of a single config field to write ({key, value} shape, \ + matching the CLI `config set` and the Windows/Linux tools). Pair with `value`. \ + Equivalent to passing the field directly." + }, + "value": { + "description": "New value for `key`. JSON type depends on the key." + }, "capture_mode": { "type": "string", "enum": ["som", "vision", "ax"], @@ -75,20 +84,36 @@ impl Tool for SetConfigTool { // sessions don't clobber each other or the persisted default. let session_id = args.opt_str("_session_id"); + // Accept BOTH shapes, matching Windows/Linux + the CLI `config set`: + // - direct fields: {"capture_scope":"desktop"} + // - {key, value}: {"key":"capture_scope","value":"desktop"} + // A direct field wins if both are somehow present. + let kv: Option<(String, Value)> = args + .opt_str("key") + .and_then(|k| args.get("value").map(|v| (k, v.clone()))); + let kv_str = |name: &str| -> Option { + kv.as_ref() + .filter(|(k, _)| k == name) + .and_then(|(_, v)| v.as_str().map(str::to_owned)) + }; + let kv_u64 = |name: &str| -> Option { + kv.as_ref().filter(|(k, _)| k == name).and_then(|(_, v)| v.as_u64()) + }; + // Validate max_image_dimension up front so both branches share the // u32 check and we never half-apply. - let max_dim: Option = match args.opt_u64("max_image_dimension") { + let max_dim: Option = match args.opt_u64("max_image_dimension").or_else(|| kv_u64("max_image_dimension")) { Some(dim) => match u32::try_from(dim) { Ok(d) => Some(d), Err(_) => return ToolResult::error(format!("max_image_dimension {dim} exceeds u32::MAX")), }, None => None, }; - let capture_mode = args.opt_str("capture_mode"); + let capture_mode = args.opt_str("capture_mode").or_else(|| kv_str("capture_mode")); // Validate capture_scope up front so both branches share the check and // we never half-apply an invalid value. - let capture_scope = args.opt_str("capture_scope"); + let capture_scope = args.opt_str("capture_scope").or_else(|| kv_str("capture_scope")); if let Some(scope) = capture_scope.as_deref() { if scope != "window" && scope != "desktop" { return ToolResult::error(format!(