script — Shell, AppleScript, JXA & PowerShell#
The fast path of computer use. Many desktop tasks don’t need to see anything: reading a file, flipping a defaults key, asking Finder for its selection, querying the registry. One script call replaces a dozen screenshot → click round-trips and gives the same answer every time.
from opendesk.tools.script import ScriptTool
tool = ScriptTool()
import { OpenDeskClient } from "@vitalops/opendesk-sdk";
const client = new OpenDeskClient();
await client.script({ action: "shell", code: "ls ~/Desktop" });
Routing rule#
The tool description tells the agent:
scriptfirst, when the task doesn’t need visual interaction: files, CLIs (git,curl,ffmpeg), macOSdefaults/open/mdfind, an app’s AppleScript dictionary (Finder, Mail, Safari, Music, System Events), Windows PowerShell / WMI / registry.ui(accessibility) when it does.mouseonly when neither works.
Actions#
Action |
Runs as |
Platforms |
|---|---|---|
|
|
all |
|
|
macOS |
|
|
macOS |
|
|
Windows (+ pwsh) |
Asking for applescript or jxa on Windows or Linux returns a clear error instead of running anything.
Parameters#
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
string |
— |
|
|
string |
— |
The command or script source |
|
number |
|
Seconds before the process is killed (max |
|
string |
null |
Working directory |
|
string |
default peer |
(MCP) run on a paired machine instead |
The result reports the exit code, stdout and stderr. A non-zero exit code is flagged as an error so the agent notices. Output over 16,000 characters per stream is cut down to its first and last parts, with a note saying how much was left out.
Ask Claude#
“How much free disk space do I have?”
“Turn on ‘show all file extensions’ in Finder”
“What’s the title of every open Safari tab?”
“List the 10 biggest files in ~/Downloads”
“Which Windows services are set to start automatically?” (PowerShell)
“Run
git pullin ~/code/api on the build-mac peer”
SDK examples#
params = ScriptTool.Params
# Shell
r = await tool.execute(ctx, params(action="shell", code="df -h /", timeout=10))
print(r.output) # Exit code: 0 … --- stdout --- …
# AppleScript (macOS) — app dictionaries beat clicking menus
await tool.execute(ctx, params(
action="applescript",
code='tell application "Finder" to get name of every window',
))
# JXA (macOS)
await tool.execute(ctx, params(
action="jxa",
code="Application('Safari').windows[0].tabs.name()",
))
# PowerShell (Windows)
await tool.execute(ctx, params(
action="powershell",
code="Get-Service | Where-Object StartType -eq Automatic | Select -First 5",
))
const r = await client.script({ action: "jxa", code: "Application('Music').currentTrack.name()" });
console.log(r.output, r.metadata.returncode);
Remote peers#
In Python the tool calls ctx.computer.exec(argv), so it runs on whichever Computer the context holds. From MCP, pass peer to run on a paired machine. The remote’s policy still gates process.exec. With serve --approve console, the operator is asked before each script runs. Shell, AppleScript and JXA source is passed in argv, so the approval prompt and the remote audit log show it. PowerShell is passed as -EncodedCommand, so they show base64 instead. The JS MCP server forwards script to the peer’s tool dispatcher the same way.
Every call is recorded in the session audit log as script: the code (first 1,000 characters), the working directory, the timeout and the exit code. See audit.
!!! warning
script runs arbitrary code with the user’s privileges. Use a permission handler (interactive_context() in Python, permissionHandler in JS) when you don’t fully trust the agent. Your handler gets the action and a preview of the code.
Next: browser →