From f1855a6ecf9486f5e187a157d4badb14ff473917 Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Tue, 23 Jun 2026 02:37:18 +0000 Subject: [PATCH 1/4] docs(CUA-628): add Qwen Code and Droid CLI to post-install hints and docs - README.md: add Qwen Code and Droid CLI to the supported clients list - connect-your-agent.mdx: add dedicated sections for Qwen Code and Droid CLI with setup instructions; update page description - install.sh: add Qwen Code and Droid CLI to the post-install MCP hint block - cli.rs: add 'qwen'/'qwen-code' and 'droid'/'droid-cli' to run_mcp_config; update client list in error message and doc comment - mcp_config_parity.rs: add parity test cases for qwen and droid clients Qwen Code (QwenLM/qwen-code) migrated its Computer Use backend to cua-driver in QwenLM/qwen-code#5051. Droid CLI (Factory AI) is an MCP-capable coding agent that supports stdio servers via 'droid mcp add ... --type stdio'. --- README.md | 2 +- .../driver/connect-your-agent.mdx | 46 ++++++++++++++++++- .../rust/crates/cua-driver/src/cli.rs | 33 +++++++++++-- .../examples/mcp_config_parity.rs | 4 +- libs/cua-driver/scripts/install.sh | 6 ++- 5 files changed, 84 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2db03ceb4b..81d3ee50b6 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ ## Cua Drivers - Background computer-use on macOS and Windows, with Linux pre-release -Drive native desktop apps **in the background**. Agents click, type, and verify without stealing the cursor or focus. Use the same CLI and MCP server on macOS and Windows from Claude Code, Cursor, Codex, OpenClaw, and custom clients. Linux support is available as a pre-release backend while platform testing is still in progress. +Drive native desktop apps **in the background**. Agents click, type, and verify without stealing the cursor or focus. Use the same CLI and MCP server on macOS and Windows from Claude Code, Cursor, Codex, Qwen Code, Droid CLI, OpenClaw, and custom clients. Linux support is available as a pre-release backend while platform testing is still in progress. **macOS / Linux** diff --git a/docs/content/docs/how-to-guides/driver/connect-your-agent.mdx b/docs/content/docs/how-to-guides/driver/connect-your-agent.mdx index e9bdb7006c..6677d200e5 100644 --- a/docs/content/docs/how-to-guides/driver/connect-your-agent.mdx +++ b/docs/content/docs/how-to-guides/driver/connect-your-agent.mdx @@ -1,6 +1,6 @@ --- title: Connect your agent -description: Register Cua Driver as an MCP server with Claude Code, Codex, Cursor, Antigravity, OpenClaw, OpenCode, Hermes, and Pi. +description: Register Cua Driver as an MCP server with Claude Code, Codex, Cursor, Antigravity, OpenClaw, OpenCode, Hermes, Qwen Code, Droid CLI, and Pi. --- import { Callout } from 'fumadocs-ui/components/callout'; @@ -205,6 +205,50 @@ Each call is one-shot and returns JSON or text on stdout, which is the shape Pi Verify: run `cua-driver --help` from the same Pi shell context. +## Qwen Code + +[Qwen Code](https://github.com/QwenLM/qwen-code) (QwenLM's coding agent) uses cua-driver for its Computer Use backend. Generate the config snippet: + +```bash +cua-driver mcp-config --client qwen +``` + +Paste the printed JSON into `~/.qwen/mcp.json`, or `.qwen/mcp.json` for project scope: + +```json +{ + "mcpServers": { + "cua-driver": { + "command": "cua-driver", + "args": ["mcp"], + "type": "stdio" + } + } +} +``` + +Verify: restart Qwen Code and confirm `cua-driver` appears in the MCP server list. + +## Droid CLI + +[Droid CLI](https://factory.ai) (Factory AI's coding agent) registers MCP servers with `droid mcp add`: + +```bash +droid mcp add cua-driver cua-driver mcp --type stdio +``` + +Or generate the equivalent config snippet: + +```bash +cua-driver mcp-config --client droid +``` + +Verify: + +```bash +droid mcp list +``` + ## Any other client For any client that accepts the standard `mcpServers` shape, print the generic config: diff --git a/libs/cua-driver/rust/crates/cua-driver/src/cli.rs b/libs/cua-driver/rust/crates/cua-driver/src/cli.rs index a546a13be6..6c002e4ae6 100644 --- a/libs/cua-driver/rust/crates/cua-driver/src/cli.rs +++ b/libs/cua-driver/rust/crates/cua-driver/src/cli.rs @@ -875,7 +875,7 @@ pub fn build_manifest() -> serde_json::Value { ] }, { "name": "mcp-config", "description": "Print the MCP server config snippet or a client-specific install command.", - "args": [ { "name": "--client", "type": "string", "description": "One of: claude, codex, cursor, hermes, antigravity, openclaw, opencode, pi. Omit for the generic snippet." } ] }, + "args": [ { "name": "--client", "type": "string", "description": "One of: claude, codex, cursor, hermes, antigravity, openclaw, opencode, qwen, droid, pi. Omit for the generic snippet." } ] }, { "name": "manifest", "description": "Emit this machine-readable description of the CLI surface.", "args": [ { "name": "--pretty", "type": "flag", "description": "Pretty-print the JSON." } ] }, @@ -936,7 +936,7 @@ pub fn build_manifest() -> serde_json::Value { /// Print the MCP server config snippet or a client-specific install command. /// /// `--client ` selects one of: claude, codex, cursor, hermes, -/// antigravity, openclaw, opencode, pi. Omit for the generic JSON snippet. +/// antigravity, openclaw, opencode, pi, qwen, droid. Omit for the generic JSON snippet. pub fn run_mcp_config(client: Option<&str>) { let binary = std::env::current_exe() .ok() @@ -1102,8 +1102,35 @@ pub fn run_mcp_config(client: Option<&str>) { exactly the shape Pi is designed around." ); } + Some("qwen") | Some("qwen-code") => { + // Qwen Code (QwenLM/qwen-code) uses cua-driver for its Computer Use + // backend. It reads MCP server configuration from ~/.qwen/mcp.json + // (user-scope) or .qwen/mcp.json in the project root. + // + // No `qwen mcp add` subcommand — paste the printed JSON into the + // config file and restart Qwen Code. + println!(r#"{{ + "mcpServers": {{ + "cua-driver": {{ + "command": "{binary}", + "args": ["mcp"], + "type": "stdio" + }} + }} +}}"#); + } + Some("droid") | Some("droid-cli") => { + // Droid CLI (Factory AI — https://factory.ai) supports a + // `droid mcp add` subcommand for stdio servers: + // + // droid mcp add [args...] --type stdio + // + // The name here is "cua-driver" to stay consistent with other + // client registrations. + println!("droid mcp add cua-driver {binary} mcp --type stdio"); + } Some(other) => { - eprintln!("Unknown client '{other}'. Valid: claude, codex, cursor, antigravity, openclaw, opencode, hermes, pi."); + eprintln!("Unknown client '{other}'. Valid: claude, codex, cursor, antigravity, openclaw, opencode, hermes, qwen, droid, pi."); process::exit(2); } } diff --git a/libs/cua-driver/rust/crates/platform-windows/examples/mcp_config_parity.rs b/libs/cua-driver/rust/crates/platform-windows/examples/mcp_config_parity.rs index 4bee36f68c..4b46d3ad5d 100644 --- a/libs/cua-driver/rust/crates/platform-windows/examples/mcp_config_parity.rs +++ b/libs/cua-driver/rust/crates/platform-windows/examples/mcp_config_parity.rs @@ -18,6 +18,8 @@ fn main() { (vec!["--client", "openclaw"], vec!["openclaw mcp set cua-driver"]), (vec!["--client", "opencode"], vec!["opencode.ai/config.json", "\"type\": \"local\""]), (vec!["--client", "hermes"], vec!["mcp_servers:", "cua-driver:"]), + (vec!["--client", "qwen"], vec!["\"type\": \"stdio\"", "\"mcpServers\""]), + (vec!["--client", "droid"], vec!["droid mcp add cua-driver"]), (vec!["--client", "pi"], vec!["does not support MCP natively"]), ]; @@ -42,7 +44,7 @@ fn main() { "unknown-client stderr wrong: {stderr:?}"); println!("unknown-client err OK"); - println!("\n✅ PASS: mcp-config 8 client paths + unknown-client error verified"); + println!("\n✅ PASS: mcp-config 10 client paths + unknown-client error verified"); } #[cfg(not(target_os = "windows"))] diff --git a/libs/cua-driver/scripts/install.sh b/libs/cua-driver/scripts/install.sh index 84bfb02698..a5440d049a 100755 --- a/libs/cua-driver/scripts/install.sh +++ b/libs/cua-driver/scripts/install.sh @@ -456,6 +456,9 @@ Next steps: • Codex (OpenAI): codex mcp add cua-driver -- $BIN_LINK mcp + • Droid CLI (Factory AI): + droid mcp add cua-driver cua-driver mcp --type stdio + • OpenClaw: cua-driver mcp-config --client openclaw @@ -472,11 +475,12 @@ Next steps: } Or inside gh copilot chat: /mcp add → type=STDIO, command=$BIN_LINK, args=mcp - • Cursor / OpenCode / Hermes / Antigravity (no add CLI — paste config): + • Cursor / OpenCode / Hermes / Antigravity / Qwen Code (no add CLI — paste config): cua-driver mcp-config --client cursor # JSON for ~/.cursor/mcp.json cua-driver mcp-config --client opencode # JSON for opencode.json cua-driver mcp-config --client hermes # YAML for ~/.hermes/config.yaml cua-driver mcp-config --client antigravity # JSON for ~/.gemini/config/mcp_config.json (shared with Antigravity IDE) + cua-driver mcp-config --client qwen # JSON for ~/.qwen/mcp.json For other clients accepting the generic mcpServers shape: cua-driver mcp-config From 23242dbfaf3e87d28bba416dd8263696b020b110 Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Tue, 23 Jun 2026 03:30:50 +0000 Subject: [PATCH 2/4] fix(CUA-628): correct Qwen Code config path and remove spurious type field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ~/.qwen/mcp.json → ~/.qwen/settings.json (actual Qwen Code config file) - Remove type:stdio from Qwen Code MCP config output (Cursor-only field) - Add qwen/droid to dump-docs client list --- .../docs/how-to-guides/driver/connect-your-agent.mdx | 5 ++--- libs/cua-driver/rust/crates/cua-driver/src/cli.rs | 12 +++++++----- libs/cua-driver/scripts/install.sh | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/content/docs/how-to-guides/driver/connect-your-agent.mdx b/docs/content/docs/how-to-guides/driver/connect-your-agent.mdx index 6677d200e5..38e3b8da05 100644 --- a/docs/content/docs/how-to-guides/driver/connect-your-agent.mdx +++ b/docs/content/docs/how-to-guides/driver/connect-your-agent.mdx @@ -213,15 +213,14 @@ Verify: run `cua-driver --help` from the same Pi shell context. cua-driver mcp-config --client qwen ``` -Paste the printed JSON into `~/.qwen/mcp.json`, or `.qwen/mcp.json` for project scope: +Paste the printed JSON into `~/.qwen/settings.json`, or `.qwen/settings.json` for project scope: ```json { "mcpServers": { "cua-driver": { "command": "cua-driver", - "args": ["mcp"], - "type": "stdio" + "args": ["mcp"] } } } diff --git a/libs/cua-driver/rust/crates/cua-driver/src/cli.rs b/libs/cua-driver/rust/crates/cua-driver/src/cli.rs index 6c002e4ae6..3d7d568da1 100644 --- a/libs/cua-driver/rust/crates/cua-driver/src/cli.rs +++ b/libs/cua-driver/rust/crates/cua-driver/src/cli.rs @@ -1104,8 +1104,11 @@ pub fn run_mcp_config(client: Option<&str>) { } Some("qwen") | Some("qwen-code") => { // Qwen Code (QwenLM/qwen-code) uses cua-driver for its Computer Use - // backend. It reads MCP server configuration from ~/.qwen/mcp.json - // (user-scope) or .qwen/mcp.json in the project root. + // backend. It reads MCP server configuration from ~/.qwen/settings.json + // (user-scope) or .qwen/settings.json in the project root. + // + // Qwen Code's MCP server schema only accepts command, args, cwd, env, + // and timeout — no "type" discriminator. Do NOT add "type": "stdio". // // No `qwen mcp add` subcommand — paste the printed JSON into the // config file and restart Qwen Code. @@ -1113,8 +1116,7 @@ pub fn run_mcp_config(client: Option<&str>) { "mcpServers": {{ "cua-driver": {{ "command": "{binary}", - "args": ["mcp"], - "type": "stdio" + "args": ["mcp"] }} }} }}"#); @@ -2084,7 +2086,7 @@ fn cli_docs_json() -> serde_json::Value { { "name": "mcp-config", "abstract": "Print MCP server config or a client-specific install command.", - "discussion": "Supported clients include claude, codex, cursor, antigravity, openclaw, opencode, hermes, and pi.", + "discussion": "Supported clients include claude, codex, cursor, antigravity, openclaw, opencode, hermes, qwen, droid, and pi.", "arguments": no_args, "options": [{"name":"client","short_name":null,"help":"Client name to print configuration for.","type":"String","default_value":null,"is_optional":true}], "flags": no_flags, diff --git a/libs/cua-driver/scripts/install.sh b/libs/cua-driver/scripts/install.sh index a5440d049a..e173e8f5f3 100755 --- a/libs/cua-driver/scripts/install.sh +++ b/libs/cua-driver/scripts/install.sh @@ -480,7 +480,7 @@ Next steps: cua-driver mcp-config --client opencode # JSON for opencode.json cua-driver mcp-config --client hermes # YAML for ~/.hermes/config.yaml cua-driver mcp-config --client antigravity # JSON for ~/.gemini/config/mcp_config.json (shared with Antigravity IDE) - cua-driver mcp-config --client qwen # JSON for ~/.qwen/mcp.json + cua-driver mcp-config --client qwen # JSON for ~/.qwen/settings.json For other clients accepting the generic mcpServers shape: cua-driver mcp-config From 35cbb89bf0ae3c71806fb9f49781fae20dc5fdf3 Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Thu, 25 Jun 2026 07:36:06 +0000 Subject: [PATCH 3/4] fix(CUA-628): update qwen parity test to match no-type config output The fix commit removed 'type: stdio' from the Qwen Code MCP config output (it's a Cursor-only field, not valid in ~/.qwen/settings.json). Update the parity test assertion to check for the correct output shape: mcpServers + args:[mcp] rather than the now-removed type:stdio field. --- .../rust/crates/platform-windows/examples/mcp_config_parity.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/cua-driver/rust/crates/platform-windows/examples/mcp_config_parity.rs b/libs/cua-driver/rust/crates/platform-windows/examples/mcp_config_parity.rs index 4b46d3ad5d..59b20097a7 100644 --- a/libs/cua-driver/rust/crates/platform-windows/examples/mcp_config_parity.rs +++ b/libs/cua-driver/rust/crates/platform-windows/examples/mcp_config_parity.rs @@ -18,7 +18,7 @@ fn main() { (vec!["--client", "openclaw"], vec!["openclaw mcp set cua-driver"]), (vec!["--client", "opencode"], vec!["opencode.ai/config.json", "\"type\": \"local\""]), (vec!["--client", "hermes"], vec!["mcp_servers:", "cua-driver:"]), - (vec!["--client", "qwen"], vec!["\"type\": \"stdio\"", "\"mcpServers\""]), + (vec!["--client", "qwen"], vec!["\"mcpServers\"", "\"args\": [\"mcp\"]"]), (vec!["--client", "droid"], vec!["droid mcp add cua-driver"]), (vec!["--client", "pi"], vec!["does not support MCP natively"]), ]; From b7160a82ac31fac4b531221fc1d353769509cd56 Mon Sep 17 00:00:00 2001 From: OpenClaw Date: Thu, 25 Jun 2026 15:32:02 +0000 Subject: [PATCH 4/4] fix(CUA-628): escape binary path in qwen JSON output and quote in droid command - Use serde_json::json! for Qwen Code MCP config to properly handle backslashes in Windows paths (same pattern as antigravity/claude) - Normalize backslashes to forward slashes in JSON output - Quote binary path in droid mcp add command to handle paths with spaces - Update parity test needles to match new serde_json pretty-print format Addresses CodeRabbit review comment on PR #1986. --- .../rust/crates/cua-driver/src/cli.rs | 28 +++++++++++++------ .../examples/mcp_config_parity.rs | 2 +- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/libs/cua-driver/rust/crates/cua-driver/src/cli.rs b/libs/cua-driver/rust/crates/cua-driver/src/cli.rs index 3d7d568da1..287935b8ef 100644 --- a/libs/cua-driver/rust/crates/cua-driver/src/cli.rs +++ b/libs/cua-driver/rust/crates/cua-driver/src/cli.rs @@ -1112,14 +1112,21 @@ pub fn run_mcp_config(client: Option<&str>) { // // No `qwen mcp add` subcommand — paste the printed JSON into the // config file and restart Qwen Code. - println!(r#"{{ - "mcpServers": {{ - "cua-driver": {{ - "command": "{binary}", - "args": ["mcp"] - }} - }} -}}"#); + // + // Forward slashes in the binary path so the JSON literal is valid on + // Windows (backslashes would need escaping inside a JSON string). + let normalised = binary.replace('\\', "/"); + let full = serde_json::json!({ + "mcpServers": { + "cua-driver": { + "command": normalised, + "args": ["mcp"], + } + } + }); + let pretty = serde_json::to_string_pretty(&full) + .unwrap_or_else(|_| full.to_string()); + println!("{pretty}"); } Some("droid") | Some("droid-cli") => { // Droid CLI (Factory AI — https://factory.ai) supports a @@ -1129,7 +1136,10 @@ pub fn run_mcp_config(client: Option<&str>) { // // The name here is "cua-driver" to stay consistent with other // client registrations. - println!("droid mcp add cua-driver {binary} mcp --type stdio"); + // + // Quote the binary path so paths containing spaces are treated as a + // single argument by the shell. + println!("droid mcp add cua-driver \"{binary}\" mcp --type stdio"); } Some(other) => { eprintln!("Unknown client '{other}'. Valid: claude, codex, cursor, antigravity, openclaw, opencode, hermes, qwen, droid, pi."); diff --git a/libs/cua-driver/rust/crates/platform-windows/examples/mcp_config_parity.rs b/libs/cua-driver/rust/crates/platform-windows/examples/mcp_config_parity.rs index 59b20097a7..7144bf633e 100644 --- a/libs/cua-driver/rust/crates/platform-windows/examples/mcp_config_parity.rs +++ b/libs/cua-driver/rust/crates/platform-windows/examples/mcp_config_parity.rs @@ -18,7 +18,7 @@ fn main() { (vec!["--client", "openclaw"], vec!["openclaw mcp set cua-driver"]), (vec!["--client", "opencode"], vec!["opencode.ai/config.json", "\"type\": \"local\""]), (vec!["--client", "hermes"], vec!["mcp_servers:", "cua-driver:"]), - (vec!["--client", "qwen"], vec!["\"mcpServers\"", "\"args\": [\"mcp\"]"]), + (vec!["--client", "qwen"], vec!["\"mcpServers\"", "\"command\":", "\"args\":"]), (vec!["--client", "droid"], vec!["droid mcp add cua-driver"]), (vec!["--client", "pi"], vec!["does not support MCP natively"]), ];