device-controller-mcp
by geojakes
README.md
# device-controller-mcp
An MCP (Model Context Protocol) server that lets **Claude Desktop** and **Claude Code** control your computer — take screenshots, click, type, manage windows, and more.
> ⚠️ **Security notice:** This server grants an AI assistant direct control over your
> mouse, keyboard, clipboard, and shell (including `run_command`, which executes
> arbitrary commands). Only run it on machines you own, prefer `--app` scoping over
> `--full`, and review actions before trusting them. See [Security](#security).
## Features
- **Two scope modes** — lock every action to a single app window (`--app`), or grant full-desktop access (`--full`).
- **Cross-platform** — macOS and Windows, with a clean platform abstraction layer.
- **Screenshot** capture (full screen or single window) returned as base64 PNG.
- **Mouse & keyboard** — clicks, typing, key combos, scrolling.
- **Window management** — list, focus, resize, minimize, maximize.
- **Clipboard** read / write.
- **Shell commands** — launch apps, run commands, check running processes.
## Requirements
- Python 3.10+
- macOS or Windows
## Installation
### With [uv](https://docs.astral.sh/uv/) (recommended)
```bash
git clone https://github.com/geojakes/device-controller-mcp.git
cd device-controller-mcp
uv sync
```
This creates a virtual environment and installs everything you need. On Windows
the `pywin32` backend is pulled in automatically. Run the server with `uv run`:
```bash
uv run device-controller-mcp --full
```
You can also run it without cloning, straight from the repo, via
[`uvx`](https://docs.astral.sh/uv/guides/tools/):
```bash
uvx --from git+https://github.com/geojakes/device-controller-mcp device-controller-mcp --full
```
**macOS (optional):** the server works out of the box using AppleScript, but
installing the Quartz extra gives faster, more accurate window bounds:
```bash
uv sync --extra macos
```
### With pip
```bash
git clone https://github.com/geojakes/device-controller-mcp.git
cd device-controller-mcp
pip install -e . # add ".[macos]" on macOS for the optional Quartz backend
```
`pywin32` (required on Windows) is selected automatically. The macOS Quartz
backend is optional — without it the server falls back to AppleScript.
## Usage
### Scoped to a single app
```bash
device-controller-mcp --app "Google Chrome"
```
All screenshots, clicks, and coordinates will be relative to Chrome's window. The server auto-focuses Chrome before every action.
### Full desktop
```bash
device-controller-mcp --full
```
Screenshots capture the whole screen and coordinates are screen-absolute.
## Registering with Claude Desktop
### Automatic (recommended)
The `install` command finds your OS's `claude_desktop_config.json`, merges in a
server entry (leaving any existing servers untouched), and writes a `.bak`
backup first:
```bash
# Full-desktop access
device-controller-mcp install --full
# Or scoped to one app
device-controller-mcp install --app "Google Chrome"
```
Useful flags: `--name KEY` to set the server key, `--command PATH` to override
the executable, `--config FILE` to target a specific file, and `--dry-run` to
preview the change without writing. Restart Claude Desktop afterward.
To remove it again:
```bash
device-controller-mcp uninstall # removes the "device-controller" key
device-controller-mcp uninstall --name device-controller-google-chrome
```
### Manual
Add an entry to your `claude_desktop_config.json` by hand.
**Config file location:**
| OS | Path |
|---|---|
| macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Windows | `%APPDATA%\Claude\claude_desktop_config.json` |
### Full-desktop mode
```json
{
"mcpServers": {
"device-controller": {
"command": "device-controller-mcp",
"args": ["--full"]
}
}
}
```
### Scoped to one app
```json
{
"mcpServers": {
"device-controller-chrome": {
"command": "device-controller-mcp",
"args": ["--app", "Google Chrome"]
}
}
}
```
> **Tip:** If you installed inside a virtualenv, use the full path to the
> executable, e.g. `"/path/to/venv/bin/device-controller-mcp"`.
## Registering with Claude Code
```bash
claude mcp add device-controller -- device-controller-mcp --full
```
Or scoped:
```bash
claude mcp add device-controller-chrome -- device-controller-mcp --app "Google Chrome"
```
## Tools reference
| Tool | Description |
|---|---|
| `screenshot` | Capture screen or app window as base64 PNG. Optional sub-region crop. |
| `mouse_click` | Click at (x, y) — configurable button and click count. |
| `mouse_move` | Move cursor to (x, y) without clicking. |
| `mouse_scroll` | Scroll up/down at a position. |
| `type_text` | Type a string. Supports clipboard-paste mode for Unicode. |
| `key_press` | Press keys or combos (`ctrl+c`, `cmd+shift+s`, ...). |
| `list_windows` | List all visible windows with title, position, and size. |
| `focus_window` | Bring a window to the foreground. |
| `resize_window` | Move and resize a window. |
| `minimize_window` | Minimize a window. |
| `maximize_window` | Maximize a window to fill the screen. |
| `clipboard_read` | Read the system clipboard. |
| `clipboard_write` | Write text to the clipboard. |
| `launch_app` | Open an application by name. |
| `run_command` | Run a shell command and return stdout/stderr/exit code. |
| `is_process_running` | Check whether a named process is running. |
## macOS permissions
On macOS you need to grant **Accessibility** access to your terminal app
(or to Claude Desktop) in **System Settings > Privacy & Security > Accessibility**.
This is required for `pyautogui` to control the mouse and keyboard.
## Project structure
```
device-controller-mcp/
├── pyproject.toml
├── uv.lock # generated by `uv lock` / `uv sync`
├── .python-version # Python pin used by uv
├── requirements.txt
├── README.md
└── src/
└── device_controller_mcp/
├── __init__.py
├── __main__.py # CLI entry point (run / install / uninstall)
├── install.py # OS-aware Claude Desktop config registration
├── server.py # FastMCP server factory
├── scope.py # Scope manager (coord translation + auto-focus)
├── platform_layer/
│ ├── __init__.py # Platform detection factory
│ ├── base.py # Abstract base class + WindowInfo
│ ├── macos.py # macOS: Quartz + AppleScript
│ └── windows.py # Windows: pywin32
└── tools/
├── __init__.py
├── screenshot.py # Screen / window capture
├── input_control.py # Mouse & keyboard
├── window_mgmt.py # Window management
├── clipboard.py # Clipboard read/write
└── shell.py # Shell commands & app launcher
```
## Development
This project uses [uv](https://docs.astral.sh/uv/). Common tasks:
```bash
uv sync # create the venv and install deps (+ dev tools)
uv run device-controller-mcp --full # run the server
uv lock # regenerate the lockfile after changing deps
uv run ruff check . # lint
```
> **Note:** `uv.lock` is committed so everyone resolves identical dependency
> versions. If it isn't present yet, run `uv lock` once and commit the result.
## Security
This server gives an AI assistant real control over your machine. Treat it
accordingly:
- **Arbitrary code execution.** The `run_command` tool runs any shell command,
and `type_text` / `key_press` can drive any application. There is no sandbox.
- **Prefer scoped mode.** `--app "<name>"` keeps screenshots and coordinates
bound to a single window. Use `--full` only when you genuinely need
whole-desktop access.
- **Run it locally and trusted.** Only register this server with clients you
control, on machines you own. Never expose it to untrusted input or networks.
- **Review before trusting.** Watch what the assistant does, especially the
first time you use it with a new workflow.
Found a vulnerability? Please open a private security advisory on GitHub rather
than a public issue.
## License
[MIT](LICENSE)
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing