@agent-sh/computer-use-linux 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -36,11 +36,13 @@ The crate was extracted from [`codex-desktop-linux`](https://github.com/avifenes
|
|
|
36
36
|
MCP tools exposed by the server:
|
|
37
37
|
|
|
38
38
|
**Diagnostics**
|
|
39
|
+
|
|
39
40
|
- `doctor` — single-shot JSON readiness report (platform, portals, accessibility, windowing, input, readiness summary, and a capability map of available backends)
|
|
40
41
|
- `setup_accessibility` — enables GNOME's `org.gnome.desktop.interface toolkit-accessibility` setting so toolkit apps expose AT-SPI trees
|
|
41
42
|
- `setup_window_targeting` — installs and enables the bundled GNOME Shell extension when `org.gnome.Shell.Introspect` is locked down
|
|
42
43
|
|
|
43
44
|
**Discovery**
|
|
45
|
+
|
|
44
46
|
- `list_apps` — running desktop apps visible to the AT-SPI registry
|
|
45
47
|
- `list_windows` — compositor windows with title, app id, wm_class, focus state, client type (Wayland/X11), and bounds
|
|
46
48
|
- `focused_window` — the window currently holding keyboard focus
|
|
@@ -50,6 +52,7 @@ MCP tools exposed by the server:
|
|
|
50
52
|
Screenshot payloads are size-bounded by default before they are returned to the MCP host: max 1920 px width/height and 2 MiB image bytes, with hard caps even when callers request more. Agents that need more detail can pass `max_width`, `max_height`, `max_bytes`, `scale`, `format: "jpeg"`, or `quality`, preferably with a window target or crop. PNG remains the default; JPEG lets callers trade lossless pixels for a smaller payload before the byte cap forces further resizing. Returned screenshot metadata includes `coordinate_width`, `coordinate_height`, `scale`, `format`, and `quality` so callers can convert from a downscaled preview to desktop coordinate pixels.
|
|
51
53
|
|
|
52
54
|
**Input**
|
|
55
|
+
|
|
53
56
|
- `click` — by element index, semantic selector, or desktop coordinate pixels
|
|
54
57
|
- `drag` — desktop coordinate drag (start / end)
|
|
55
58
|
- `scroll` — page-based scroll on an element or at a pixel location
|
|
@@ -59,10 +62,12 @@ Screenshot payloads are size-bounded by default before they are returned to the
|
|
|
59
62
|
Targeted `press_key`/`type_text` results append focused-element feedback from AT-SPI (role, name, editable) and warn when no editable element holds focus. Click/screenshot/input results warn when the target window or coordinate is partially or fully off-screen. `get_app_state` returns a compact readiness block by default; pass `verbose: true` for the full diagnostics report.
|
|
60
63
|
|
|
61
64
|
**Semantic actions**
|
|
65
|
+
|
|
62
66
|
- `perform_action` — invoke any AT-SPI action exposed by an element (`Press`, `Activate`, `Toggle`, …); defaults to the primary action
|
|
63
67
|
- `set_value` — write to a settable accessibility element (text fields, sliders, spinners)
|
|
64
68
|
|
|
65
69
|
**Navigation**
|
|
70
|
+
|
|
66
71
|
- `activate_window` — focus a window by `window_id`, `pid`, `app_id`, `wm_class`, `title`, or terminal selectors
|
|
67
72
|
- `move_window` / `resize_window` — reposition or resize a window in desktop coordinates (GNOME Shell extension backend); useful to recover windows that are partially off-screen
|
|
68
73
|
|
|
@@ -223,6 +228,24 @@ Edit `~/.config/Claude/claude_desktop_config.json`:
|
|
|
223
228
|
|
|
224
229
|
Restart Claude Desktop. The tools should appear in the tools list.
|
|
225
230
|
|
|
231
|
+
### Pi Coding Agent
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
pi install npm:pi-mcp-adapter
|
|
235
|
+
pi install npm:@agent-sh/computer-use-linux
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Restart pi or run `/reload`. The MCP proxy tool `mcp()` will have the desktop tools available:
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
mcp({ server: "computer-use-linux" }) # list all tools
|
|
242
|
+
mcp({ search: "windows" }) # search for window tools
|
|
243
|
+
mcp({ tool: "computer_use_linux_doctor" }) # run readiness check
|
|
244
|
+
mcp({ tool: "computer_use_linux_list_windows" }) # list desktop windows
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The extension auto-registers the computer-use-linux MCP server into pi-mcp-adapter's config. If the binary is not found, check the [Pi setup guide](skills/computer-use-linux/references/pi-setup.md).
|
|
248
|
+
|
|
226
249
|
### Hermes Agent
|
|
227
250
|
|
|
228
251
|
Install the companion Hermes skill so Hermes has the desktop-specific runbook:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-sh/computer-use-linux",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Linux desktop-control MCP server: AT-SPI accessibility trees, Wayland/X11 input, screenshots, and compositor window targeting.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"wayland",
|
|
20
20
|
"accessibility",
|
|
21
21
|
"hermes-agent",
|
|
22
|
-
"codex"
|
|
22
|
+
"codex",
|
|
23
|
+
"pi-package"
|
|
23
24
|
],
|
|
24
25
|
"os": [
|
|
25
26
|
"linux"
|
|
@@ -35,10 +36,16 @@
|
|
|
35
36
|
"LICENSE",
|
|
36
37
|
"README.md",
|
|
37
38
|
"skills/computer-use-linux/SKILL.md",
|
|
39
|
+
"skills/computer-use-linux/references/",
|
|
38
40
|
"npm/README.md",
|
|
39
41
|
"npm/bin/computer-use-linux.js",
|
|
40
|
-
"npm/install.js"
|
|
42
|
+
"npm/install.js",
|
|
43
|
+
"pi/"
|
|
41
44
|
],
|
|
45
|
+
"pi": {
|
|
46
|
+
"extensions": ["./pi/extension/index.ts"],
|
|
47
|
+
"skills": ["./skills/computer-use-linux/SKILL.md"]
|
|
48
|
+
},
|
|
42
49
|
"scripts": {
|
|
43
50
|
"postinstall": "node npm/install.js",
|
|
44
51
|
"pack:check": "npm pack --dry-run",
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi coding agent extension for computer-use-linux.
|
|
3
|
+
*
|
|
4
|
+
* Discovers the computer-use-linux binary and registers it as an MCP server
|
|
5
|
+
* for pi-mcp-adapter by writing to ~/.pi/agent/mcp.json.
|
|
6
|
+
*
|
|
7
|
+
* Prerequisite: pi-mcp-adapter (npm:pi-mcp-adapter) must be installed.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
11
|
+
import {
|
|
12
|
+
accessSync,
|
|
13
|
+
constants,
|
|
14
|
+
existsSync,
|
|
15
|
+
mkdirSync,
|
|
16
|
+
readFileSync,
|
|
17
|
+
writeFileSync,
|
|
18
|
+
} from "node:fs";
|
|
19
|
+
import { homedir } from "node:os";
|
|
20
|
+
import { dirname, join } from "node:path";
|
|
21
|
+
import { env } from "node:process";
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Constants
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
const PACKAGE_NAME = "@agent-sh/computer-use-linux";
|
|
28
|
+
const MCP_SERVER_NAME = "computer-use-linux";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Path to pi's agent-level MCP config. pi-mcp-adapter reads from this file
|
|
32
|
+
* when it starts up, alongside ~/.config/mcp/mcp.json and .mcp.json.
|
|
33
|
+
*/
|
|
34
|
+
function getPiAgentMcpConfigPath(): string {
|
|
35
|
+
const configured = env.PI_CODING_AGENT_DIR?.trim();
|
|
36
|
+
if (configured) {
|
|
37
|
+
return join(configured, "mcp.json");
|
|
38
|
+
}
|
|
39
|
+
return join(homedir(), ".pi", "agent", "mcp.json");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Binary discovery
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Find the computer-use-linux binary. Cached after first successful lookup.
|
|
48
|
+
*/
|
|
49
|
+
let _binaryPath: string | null | undefined;
|
|
50
|
+
|
|
51
|
+
function findBinary(): string | null {
|
|
52
|
+
if (_binaryPath !== undefined) return _binaryPath;
|
|
53
|
+
|
|
54
|
+
// 1) Environment variable override
|
|
55
|
+
const fromEnv = env.COMPUTER_USE_LINUX_BIN;
|
|
56
|
+
if (fromEnv && existsSync(fromEnv)) {
|
|
57
|
+
_binaryPath = fromEnv;
|
|
58
|
+
return fromEnv;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 2) Bundled by the npm wrapper's postinstall
|
|
62
|
+
// The npm wrapper downloads binaries to npm/bin/ next to the JS wrapper.
|
|
63
|
+
// We derive the path relative to our own installed location.
|
|
64
|
+
const candidates = [
|
|
65
|
+
// Bundled binary for this platform
|
|
66
|
+
join(
|
|
67
|
+
__dirname,
|
|
68
|
+
"..",
|
|
69
|
+
"..",
|
|
70
|
+
"npm",
|
|
71
|
+
"bin",
|
|
72
|
+
`computer-use-linux-linux-${process.arch}`,
|
|
73
|
+
),
|
|
74
|
+
// Direct npm global install
|
|
75
|
+
join(__dirname, "..", "..", "npm", "bin", "computer-use-linux"),
|
|
76
|
+
];
|
|
77
|
+
for (const candidate of candidates) {
|
|
78
|
+
const resolved = join(candidate);
|
|
79
|
+
if (existsSync(resolved)) {
|
|
80
|
+
_binaryPath = resolved;
|
|
81
|
+
return resolved;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 3) PATH
|
|
86
|
+
const pathDirs = (env.PATH || "").split(":");
|
|
87
|
+
for (const dir of pathDirs) {
|
|
88
|
+
const candidate = join(dir, "computer-use-linux");
|
|
89
|
+
try {
|
|
90
|
+
if (existsSync(candidate)) {
|
|
91
|
+
accessSync(candidate, constants.X_OK);
|
|
92
|
+
_binaryPath = candidate;
|
|
93
|
+
return candidate;
|
|
94
|
+
}
|
|
95
|
+
} catch {
|
|
96
|
+
// Skip inaccessible or non-executable entries
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
_binaryPath = null;
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// MCP config management
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
interface McpServerEntry {
|
|
109
|
+
command: string;
|
|
110
|
+
args: string[];
|
|
111
|
+
lifecycle?: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
interface McpConfig {
|
|
115
|
+
mcpServers?: Record<string, McpServerEntry>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
119
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function readMcpConfig(path: string): McpConfig | null {
|
|
123
|
+
// Only return empty if the file genuinely doesn't exist.
|
|
124
|
+
// If the file exists but can't be read or parsed, return null
|
|
125
|
+
// so the caller knows not to overwrite it.
|
|
126
|
+
if (!existsSync(path)) return {};
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const raw = readFileSync(path, "utf-8");
|
|
130
|
+
const parsed = JSON.parse(raw);
|
|
131
|
+
if (!isRecord(parsed)) {
|
|
132
|
+
console.error(
|
|
133
|
+
`${PACKAGE_NAME}: MCP config at ${path} contains a ${typeof parsed}` +
|
|
134
|
+
(Array.isArray(parsed) ? " (array)" : "") +
|
|
135
|
+
", expected a JSON object. Skipping.",
|
|
136
|
+
);
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
return parsed as McpConfig;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error(
|
|
142
|
+
`${PACKAGE_NAME}: failed to read existing MCP config at ${path}:`,
|
|
143
|
+
error instanceof Error ? error.message : String(error),
|
|
144
|
+
);
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function writeMcpConfig(path: string, config: McpConfig): void {
|
|
150
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
151
|
+
writeFileSync(path, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Result of ensuring the computer-use-linux entry in the MCP config.
|
|
156
|
+
* - "updated": entry was written or updated successfully
|
|
157
|
+
* - "unchanged": entry already matches, no write needed
|
|
158
|
+
* - "failed": config could not be read or written
|
|
159
|
+
*/
|
|
160
|
+
type EnsureResult = "updated" | "unchanged" | "failed";
|
|
161
|
+
|
|
162
|
+
function ensureServerEntry(
|
|
163
|
+
configPath: string,
|
|
164
|
+
binaryPath: string,
|
|
165
|
+
): EnsureResult {
|
|
166
|
+
const config = readMcpConfig(configPath);
|
|
167
|
+
|
|
168
|
+
// If config exists but can't be read, don't overwrite it
|
|
169
|
+
if (config === null) return "failed";
|
|
170
|
+
|
|
171
|
+
// Validate that mcpServers is a plain object before mutating
|
|
172
|
+
const servers =
|
|
173
|
+
config.mcpServers != null && isRecord(config.mcpServers)
|
|
174
|
+
? (config.mcpServers as Record<string, McpServerEntry>)
|
|
175
|
+
: {};
|
|
176
|
+
|
|
177
|
+
const existing = servers[MCP_SERVER_NAME];
|
|
178
|
+
|
|
179
|
+
// Check if entry already exists and matches
|
|
180
|
+
if (
|
|
181
|
+
existing &&
|
|
182
|
+
existing.command === binaryPath &&
|
|
183
|
+
existing.args?.length === 1 &&
|
|
184
|
+
existing.args[0] === "mcp"
|
|
185
|
+
) {
|
|
186
|
+
return "unchanged"; // No change needed
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Add or update the entry
|
|
190
|
+
servers[MCP_SERVER_NAME] = {
|
|
191
|
+
command: binaryPath,
|
|
192
|
+
args: ["mcp"],
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
writeMcpConfig(configPath, { ...config, mcpServers: servers });
|
|
197
|
+
return "updated"; // Config was updated
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error(
|
|
200
|
+
`${PACKAGE_NAME}: failed to write MCP config to ${configPath}:`,
|
|
201
|
+
error instanceof Error ? error.message : String(error),
|
|
202
|
+
);
|
|
203
|
+
return "failed"; // Write failed
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// Extension entry point
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
export default function (pi: ExtensionAPI) {
|
|
212
|
+
// Resolve binary path eagerly (before session_start) so it's cached.
|
|
213
|
+
const binaryPath = findBinary();
|
|
214
|
+
|
|
215
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
216
|
+
try {
|
|
217
|
+
if (!binaryPath) {
|
|
218
|
+
ctx.ui.notify?.(
|
|
219
|
+
`${PACKAGE_NAME}: computer-use-linux binary not found. ` +
|
|
220
|
+
"Install it with 'npm install -g @agent-sh/computer-use-linux' " +
|
|
221
|
+
"or set COMPUTER_USE_LINUX_BIN.",
|
|
222
|
+
"warning",
|
|
223
|
+
);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Write/update the MCP config that pi-mcp-adapter reads
|
|
228
|
+
const configPath = getPiAgentMcpConfigPath();
|
|
229
|
+
const result = ensureServerEntry(configPath, binaryPath);
|
|
230
|
+
|
|
231
|
+
if (result === "updated" && ctx.hasUI) {
|
|
232
|
+
ctx.ui.notify?.(
|
|
233
|
+
`${PACKAGE_NAME}: MCP server configured at ${configPath}. ` +
|
|
234
|
+
"Run /reload if pi-mcp-adapter is already installed.",
|
|
235
|
+
"info",
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (result === "failed" && ctx.hasUI) {
|
|
240
|
+
ctx.ui.notify?.(
|
|
241
|
+
`${PACKAGE_NAME}: failed to configure MCP server at ${configPath}. ` +
|
|
242
|
+
"Check the console logs and ensure the file is writable.",
|
|
243
|
+
"error",
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Check that pi-mcp-adapter is available (its tools are registered)
|
|
248
|
+
if (!pi.getAllTools().some((t) => t.name === "mcp")) {
|
|
249
|
+
ctx.ui.notify?.(
|
|
250
|
+
`${PACKAGE_NAME}: pi-mcp-adapter not detected. ` +
|
|
251
|
+
"Install it with 'pi install npm:pi-mcp-adapter' then /reload.",
|
|
252
|
+
"warning",
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.error(
|
|
257
|
+
`${PACKAGE_NAME}: unexpected error in session_start handler:`,
|
|
258
|
+
error instanceof Error ? error.message : String(error),
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: computer-use-linux
|
|
3
|
-
description: "
|
|
3
|
+
description: "Linux desktop observation and control via the computer-use-linux MCP server: accessibility trees, screenshots, window targeting, and input synthesis (click, type, scroll). Works with any MCP host."
|
|
4
4
|
author: agent-sh
|
|
5
5
|
license: MIT
|
|
6
6
|
platforms: [linux]
|
|
@@ -8,28 +8,29 @@ platforms: [linux]
|
|
|
8
8
|
|
|
9
9
|
# computer-use-linux
|
|
10
10
|
|
|
11
|
-
Use `computer-use-linux` when
|
|
11
|
+
Use `computer-use-linux` when an agent needs to observe or operate a local Linux desktop through MCP: inspect the accessibility tree, list/focus windows, take screenshots, click, scroll, type, press keys, or invoke AT-SPI actions.
|
|
12
12
|
|
|
13
13
|
## When to Use
|
|
14
14
|
|
|
15
15
|
Use this skill when:
|
|
16
|
-
|
|
16
|
+
|
|
17
|
+
- The user wants the agent to control a Linux GUI app.
|
|
17
18
|
- You need desktop state from AT-SPI, screenshots, or compositor window metadata.
|
|
18
|
-
- You are configuring the `computer-use-linux` MCP server for
|
|
19
|
+
- You are configuring the `computer-use-linux` MCP server for your agent.
|
|
19
20
|
- A desktop action needs target-aware input instead of blind shell commands.
|
|
20
21
|
|
|
21
22
|
Do not use this for remote browsers, websites, or headless automation when a browser-specific tool is available. Do not assume desktop actions are safe just because the MCP connection works.
|
|
22
23
|
|
|
23
24
|
## Install
|
|
24
25
|
|
|
25
|
-
Preferred install
|
|
26
|
+
Preferred install:
|
|
26
27
|
|
|
27
28
|
```bash
|
|
28
29
|
npm install -g @agent-sh/computer-use-linux
|
|
29
30
|
computer-use-linux doctor | jq .readiness
|
|
30
31
|
```
|
|
31
32
|
|
|
32
|
-
Rust users can install
|
|
33
|
+
Rust users can install from crates.io:
|
|
33
34
|
|
|
34
35
|
```bash
|
|
35
36
|
cargo install computer-use-linux
|
|
@@ -47,42 +48,23 @@ computer-use-linux doctor | jq .readiness
|
|
|
47
48
|
|
|
48
49
|
On GNOME Wayland, log out and back in after `setup-window-targeting` if the GNOME Shell extension was newly installed.
|
|
49
50
|
|
|
50
|
-
## Configure
|
|
51
|
+
## Configure Your Agent
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
The `computer-use-linux` binary is an MCP server. Configure it as a stdio MCP server in your agent of choice:
|
|
53
54
|
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"command": "computer-use-linux",
|
|
58
|
+
"args": ["mcp"]
|
|
59
|
+
}
|
|
58
60
|
```
|
|
59
61
|
|
|
60
|
-
`
|
|
61
|
-
|
|
62
|
-
The generated config should look like this:
|
|
63
|
-
|
|
64
|
-
```yaml
|
|
65
|
-
mcp_servers:
|
|
66
|
-
computer-use-linux:
|
|
67
|
-
command: computer-use-linux
|
|
68
|
-
args: ["mcp"]
|
|
69
|
-
timeout: 120
|
|
70
|
-
connect_timeout: 30
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
If the binary is not on `PATH`, pass the absolute path to `--command`.
|
|
74
|
-
|
|
75
|
-
Hermes registers tools using the `mcp_<server>_<tool>` pattern. With this config, tool names are prefixed as `mcp_computer_use_linux_`, for example:
|
|
62
|
+
If the binary is not on `PATH`, use the absolute path (typically `~/.local/bin/computer-use-linux` or the npm global bin directory).
|
|
76
63
|
|
|
77
|
-
|
|
78
|
-
| --- | --- |
|
|
79
|
-
| `doctor` | `mcp_computer_use_linux_doctor` |
|
|
80
|
-
| `get_app_state` | `mcp_computer_use_linux_get_app_state` |
|
|
81
|
-
| `list_windows` | `mcp_computer_use_linux_list_windows` |
|
|
82
|
-
| `click` | `mcp_computer_use_linux_click` |
|
|
83
|
-
| `type_text` | `mcp_computer_use_linux_type_text` |
|
|
64
|
+
### Host-specific guides
|
|
84
65
|
|
|
85
|
-
|
|
66
|
+
- [Hermes setup](references/hermes-setup.md)
|
|
67
|
+
- [Pi coding agent setup](references/pi-setup.md)
|
|
86
68
|
|
|
87
69
|
## Procedure
|
|
88
70
|
|
|
@@ -110,7 +92,6 @@ Run:
|
|
|
110
92
|
|
|
111
93
|
```bash
|
|
112
94
|
computer-use-linux doctor | jq .readiness
|
|
113
|
-
hermes chat --toolsets mcp-computer-use-linux -q "List the current desktop windows."
|
|
114
95
|
```
|
|
115
96
|
|
|
116
97
|
Ready output should have:
|
|
@@ -121,4 +102,4 @@ Ready output should have:
|
|
|
121
102
|
- `can_send_development_input: true`
|
|
122
103
|
- `blockers: []`
|
|
123
104
|
|
|
124
|
-
|
|
105
|
+
Then test with your agent by calling the `doctor` tool or asking the agent to list desktop windows.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hermes-setup
|
|
3
|
+
description: "Hermes agent setup for the computer-use-linux MCP server."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Hermes Setup
|
|
7
|
+
|
|
8
|
+
Add the server with the Hermes MCP CLI:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
hermes mcp add computer-use-linux --command computer-use-linux --args mcp
|
|
12
|
+
hermes mcp test computer-use-linux
|
|
13
|
+
hermes mcp configure computer-use-linux
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`configure` opens Hermes' tool-selection UI for this MCP server.
|
|
17
|
+
|
|
18
|
+
The generated config should look like this:
|
|
19
|
+
|
|
20
|
+
```yaml
|
|
21
|
+
mcp_servers:
|
|
22
|
+
computer-use-linux:
|
|
23
|
+
command: computer-use-linux
|
|
24
|
+
args: ["mcp"]
|
|
25
|
+
timeout: 120
|
|
26
|
+
connect_timeout: 30
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If the binary is not on `PATH`, pass the absolute path to `--command`.
|
|
30
|
+
|
|
31
|
+
Hermes registers tools using the `mcp_<server>_<tool>` pattern. With this config, tool names are prefixed as `mcp_computer_use_linux_`, for example:
|
|
32
|
+
|
|
33
|
+
| MCP tool | Hermes tool name |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| `doctor` | `mcp_computer_use_linux_doctor` |
|
|
36
|
+
| `get_app_state` | `mcp_computer_use_linux_get_app_state` |
|
|
37
|
+
| `list_windows` | `mcp_computer_use_linux_list_windows` |
|
|
38
|
+
| `click` | `mcp_computer_use_linux_click` |
|
|
39
|
+
| `type_text` | `mcp_computer_use_linux_type_text` |
|
|
40
|
+
|
|
41
|
+
Restart Hermes after changing MCP config.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pi-setup
|
|
3
|
+
description: "Pi coding agent setup for the computer-use-linux MCP server."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pi Setup
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
|
|
10
|
+
You need both of these installed:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pi install npm:pi-mcp-adapter
|
|
14
|
+
pi install npm:@agent-sh/computer-use-linux
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
> **Note:** `pi-mcp-adapter` is a separate extension that provides MCP protocol support in pi. The `@agent-sh/computer-use-linux` package auto-registers the desktop MCP server into pi-mcp-adapter's config.
|
|
18
|
+
|
|
19
|
+
After installing both, restart pi or run `/reload`.
|
|
20
|
+
|
|
21
|
+
## Verify
|
|
22
|
+
|
|
23
|
+
The `mcp()` proxy tool should now be available. Call it from any prompt:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
mcp({ search: "doctor" })
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or start with the readiness check (note the server-prefixed tool name):
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
mcp({ tool: "computer_use_linux_doctor" })
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Search for available tools:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
mcp({ server: "computer-use-linux" })
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## If the binary is not found
|
|
42
|
+
|
|
43
|
+
The extension looks for `computer-use-linux` in this order:
|
|
44
|
+
|
|
45
|
+
1. `COMPUTER_USE_LINUX_BIN` environment variable
|
|
46
|
+
2. The npm-bundled binary (from `npm install -g @agent-sh/computer-use-linux`)
|
|
47
|
+
3. `$PATH`
|
|
48
|
+
|
|
49
|
+
If none are found, install it:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npm install -g @agent-sh/computer-use-linux
|
|
53
|
+
# or
|
|
54
|
+
cargo install computer-use-linux
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Then restart pi or run `/reload`.
|