Claude Code’s Computer Use feature lets AI directly operate the desktop environment: clicking buttons, typing text, taking screenshots to analyze interface state. Giving AI screen access plus mouse and keyboard control introduces real risks: accidental file deletion, misclicks, sensitive data leaks. To address this, Claude Code implements a nine-layer security gate system where each layer can independently intercept dangerous operations. Under the hood, a Python Bridge handles the cross-language work — a TypeScript proxy drives a Python executor that performs the actual desktop interactions.
Overall Architecture and the Python Choice
Computer Use follows a typical cross-language proxy pattern. Claude Code (TypeScript) handles strategy decisions and permission control; a Python process executes the actual desktop operations. The two communicate via JSON-RPC over stdio. The split keeps responsibilities clear: TypeScript handles security and model interaction, Python handles platform API calls.
1 2 3 4 5
Claude Code (TypeScript) ↓ JSON-RPC over stdio Python Bridge (computer_controller.py) ↓ Platform Abstraction Desktop Environment
The core reason for choosing Python over TypeScript native for desktop operations is ecosystem maturity. Libraries like pyautogui, PyObjC, and xdotool have been running stably for years with consistent cross-platform interfaces. Implementing desktop operations in TypeScript through native addons would significantly increase maintenance cost.
Factor
Python Approach
TypeScript Native
Library ecosystem
pyautogui/PyObjC — mature and stable
Requires native addons, fragmented ecosystem
Cross-platform consistency
Unified interface across 3 platforms
Each platform needs separate wrapping
Development iteration speed
Pure Python, fast to modify
Compile native code, slow iteration
Maintenance cost
Community-maintained, frequent updates
Self-maintained, adapt to each system’s API changes
24 Desktop Operation Tools
Computer Use provides 24 tools covering four categories: input, display, file, and process. Input tools handle mouse clicks, double-clicks, drag-and-drop, scroll wheel, keyboard shortcuts, single key presses, text input, and clipboard paste. Display tools handle screenshots, screen dimensions, window list queries, window activation, and window position/size retrieval. File tools include read, write, delete, list directory, move, copy, and file info. Process tools provide process list queries, launching new processes, and terminating processes.
Read/write/delete/list/move/copy, file info queries
Process
3
Process list, launch process, terminate process
Taking the mouse click tool as an example, tool definitions describe parameter structure via JSON Schema, including coordinates, button type, and click count. Claude Code registers these definitions as callable tools, and the model initiates calls through tool_use when desktop operations are needed.
Security design is the heart of the Computer Use system. Nine gates progress from outside to inside, each capable of independently intercepting operations. This isn’t theoretical — it’s an actual check chain in the code. Operations must pass all gates to execute; any gate returning deny immediately terminates the operation.
Gate
Name
Interception Rule
Gate 1
Feature gate
tengu_computer_use Feature Flag must be enabled
Gate 2
User consent
First-time use shows confirmation dialog, user must authorize
Mouse coordinates must be within screen resolution; window operations must target visible windows
Gate 7
Rate limiting
Max 10 operations per second; 3 consecutive failures trigger auto-pause
Gate 8
Screenshot content analysis
Detects password fields, private info, and other sensitive content; detects error dialogs
Gate 9
Real-time monitoring
User can Ctrl+C interrupt at any time; operation logs output in real time
In code, the gateComputerUseAction function executes all checks in order, returning allow, deny, or ask. The first seven gates complete before execution; Gate 8 runs after screenshot capture; Gate 9 spans the entire operation lifecycle.
// Gate 8: Screenshot Analysis (performed after capture) // Gate 9: Real-time Monitoring (handled by interrupt mechanism)
return { action: 'allow' } }
Cross-Language Communication Bridge
Communication between TypeScript and Python is based on JSON-RPC 2.0 protocol over stdio transport. The TypeScript side constructs standard JSON-RPC requests (with method, params, id), writes them to the Python process’s stdin, then reads responses from stdout. This design avoids HTTP overhead while keeping the protocol standardized.
// Write to stdin bridgeProcess.stdin.write(JSON.stringify(message) + '\n')
// Read from stdout const response = await readBridgeResponse()
if (response.error) { thrownew BridgeError(response.error.code, response.error.message) }
return response.result }
The Python-side ComputerController maintains a method-name-to-handler mapping table, loops reading JSON-RPC requests from stdin, dispatches to the corresponding handler, and writes results or errors back to stdout. Each request is handled independently — exceptions don’t crash the entire process.
Screenshots are Computer Use’s primary way of perceiving the environment. When the model decides to take a screenshot, the computer_screen_capture tool calls pyautogui.screenshot() through the Python Bridge, encodes the PNG as Base64, and returns it to Claude Code. It gets injected as an image block into the current conversation context, where the model analyzes it through multimodal capabilities. Before injection, the system runs local OCR to detect sensitive keywords (password, secret, api key, token, etc.) and blurs sensitive regions if any are found.
Window management involves handling API differences across three platforms. The system defines a unified WindowInfo interface (with id, title, process, position, size, visible fields), and each platform adapter converts native window info into this format. Before activating a window, the system checks if it belongs to a sensitive application (password managers, banking apps, etc.) and refuses activation if so.
All Computer Use operations are logged to an audit trail, including timestamp, action type, parameters, execution result (success/deny/error), rejection reason, and duration, with optional post-action screenshots. Logs persist in JSONL format to .claude/computer_use_history.jsonl for post-incident investigation and debugging.
The interrupt mechanism is the last line of defense in the security system. When the user presses Ctrl+C, the TypeScript process notifies the Python Bridge to stop execution, moves the mouse to a safe position, and records the interrupt event in the audit log. The Python side maintains an emergency_stop flag — upon receiving the stop signal, it exits the main loop and restores the mouse to preset safe coordinates. This design ensures the user can immediately reclaim control even in extreme situations.
defstop(self): self.emergency_stop = True # Restore mouse to safe position pyautogui.moveTo(self.safe_x, self.safe_y)
Three-Platform Adaptation
Window operation APIs differ significantly across the three platforms. Windows uses ctypes to call Win32 APIs (GetForegroundWindow, GetWindowTextW). macOS uses PyObjC to access NSWorkspace. Linux depends on the xdotool command-line tool. Each platform adapter must implement the full interface: window retrieval, activation, position queries, and size queries.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# platform/windows.py import pyautogui import ctypes from ctypes import wintypes
defget_active_window(): """Get the active window""" hwnd = ctypes.windll.user32.GetForegroundWindow() return hwnd
# platform/macos.py import pyautogui from AppKit import NSWorkspace, NSRunningApplication
defget_active_window(): """Get the active window""" workspace = NSWorkspace.sharedWorkspace() app = workspace.activeApplication() return app.localizedName()
defactivate_window(title): """Activate a window""" workspace = NSWorkspace.sharedWorkspace() apps = workspace.runningApplications() for app in apps: if app.localizedName() == title: app.activateWithOptions_(NSApplicationActivateIgnoringOtherApps) break
defget_active_window(): """Get the active window""" result = subprocess.run( ['xdotool', 'getactivewindow'], capture_output=True, text=True ) return int(result.stdout.strip())