Skip to main content
Glama
Bra1nZ
by Bra1nZ
README.md
# screenshot-mcp

MCP-сервер для **KDE Plasma 6 / Wayland**: агент открывает настоящее окно консоли,
управляет им и снимает рабочий стол штатным средством системы (Spectacle — тем же,
что вызывается по PrintScreen).

Ключевая сложность в том, что под Wayland не работают привычные приёмы автоматизации
терминала: `xdotool` не вводит текст в нативные Wayland-приложения, `wmctrl` не
существует, а `tmux` умеет управлять сессией, но не окном на экране. Здесь ввод идёт
**прямо в псевдотерминал**, поэтому терминал показывает его так же, как ввод с
клавиатуры, — без эмуляции клавиш и без каких-либо следов в интерфейсе.

Возможности:

* новое отдельное окно консоли с **твоим** `~/.bashrc` — твой промпт, цвета, алиасы;
* ввод команд и чтение вывода с кодом возврата;
* интерактивные программы: ввод, `Ctrl+C`, `Ctrl+D`, стрелки, история команд;
* снимок всего рабочего стола с поднятием окна консоли на передний план;
* снимок возвращается агенту прямо в ответе (картинкой), так что он может сам
  оценить, что попало в кадр.

## Как это устроено

```
MCP-сервер (server.py)
   │  UNIX-сокет, JSON
   ▼
bridge.py — «насос»: переливает ввод и вывод
   │  псевдотерминал (pty)
   ▼
bash ── запущен прямо в окне Konsole, наследует ~/.bashrc
```

* Настоящий `bash` работает **внутри** окна Konsole и подхватывает пользовательский
  `.bashrc`. Заголовок окна при этом неотличим от обычного (`<каталог> : bash — Konsole`).
* Агент пишет байтами прямо в pty. Их обрабатывает tty-драйвер (отражение ввода) и
  `readline` — то есть это буквально тот же путь, что и при наборе с клавиатуры.
  Никакой эмуляции клавиш, поэтому в терминале не остаётся следов автоматизации.
* Код возврата берётся через хук `PROMPT_COMMAND`, который пишет маркер в служебный
  файл: на экране ничего не появляется.
* Окно поднимается на передний план KWin-скриптом: окно находится по PID процесса
  Konsole, активация — `workspace.activeWindow = client`.
* Снимок делает `spectacle -b -n -o <file>` — тот же бинарник, что Plasma запускает
  по PrintScreen, поэтому картинка неотличима от сделанной вручную.

## Требования

* KDE Plasma 6 в сессии Wayland;
* `konsole` и `spectacle` (в Plasma 6 идут вместе с ней);
* `qdbus6` — для скриптинга KWin;
* Python 3.11+ и пакет `mcp`.

Проверено на Debian 13, KDE Plasma 6.3, Wayland.

## Установка

```bash
git clone git@github.com:Bra1nZ/screenshot-mcp.git
cd screenshot-mcp
pip install -r requirements.txt
```

## Подключение

### opencode

`~/.config/opencode/opencode.json`:

```json
{
  "mcp": {
    "screenshot-mcp": {
      "command": ["python3", "/путь/к/screenshot-mcp/server.py"],
      "type": "local"
    }
  }
}
```

### Другие MCP-клиенты

Сервер говорит на stdio, поэтому подходит любому клиенту с поддержкой локальных
MCP-серверов. Команда запуска:

```bash
python3 /путь/к/screenshot-mcp/server.py
```

Сервер запускается **в графической сессии** и наследует `WAYLAND_DISPLAY` и
`DBUS_SESSION_BUS_ADDRESS`; если переменных нет, подставляются значения по умолчанию
для текущего пользователя.

## Инструменты

| Инструмент | Назначение |
|---|---|
| `console_open(cwd, cols, rows, title, terminal)` | открыть новое окно консоли, вернуть `console_id` |
| `console_exec(console_id, command, timeout, typing_delay_ms)` | напечатать команду и дождаться результата: `output`, `exit_code`, `timed_out` |
| `console_send(console_id, text, enter)` | напечатать текст без выполнения — для программ, ждущих ввода |
| `console_key(console_id, key)` | спецклавиши: `enter`, `tab`, `esc`, `up`/`down`, `ctrl+c`, `ctrl+d`, `f1`..`f12` |
| `console_read(console_id, cursor)` | дочитать новый вывод |
| `console_state(console_id)` / `console_list()` | состояние консолей |
| `console_close(console_id)` | закрыть окно |
| `desktop_screenshot(path, console_id, delay_ms, preview)` | снимок рабочего стола; `console_id` — сначала поднять окно; возвращает метаданные и уменьшенную копию картинки |
| `desktop_focus(console_id)` | поднять окно консоли на передний план |
| `desktop_active_window()` / `desktop_list_windows()` | что сейчас на переднем плане, список окон |

## Пример сессии

```
console_open(cwd="~/projects/lab")        → console_id
console_exec(console_id, "make")          → output, exit_code
console_exec(console_id, "ls -la build/")  → убедиться, что артефакты на месте
desktop_screenshot(console_id=console_id)  → PNG в ~/Pictures/Screenshots + картинка в ответе
```

## Ограничения

* **Только KDE Plasma 6 / Wayland.** Скриншоты и консоль работают и под X11, но
  поднятие окна на передний план реализовано через KWin-скриптинг, то есть только
  под KWin.
* **Экран должен быть разблокирован.** Пока активен lock screen, KWin не отдаёт
  снимки — это защита композитора. `desktop_screenshot` в этом случае вернёт понятное
  сообщение.
* Символ табуляции в командах не поддерживается: `bash` воспринимает его как запрос
  автодополнения.
* `console_exec` ждёт нового промпта; для долгих задач нужен `timeout`, для
  интерактивных программ — `console_send` и `console_key`.

## Безопасность

Сервер даёт подключённому агенту **полный контроль над консолью в графической сессии
и чтение содержимого экрана**. Это инструмент автоматизации, а не средство
изоляции: запускай его только с тем агентом, которому доверяешь, и не оставляй
незакрытые окна консоли с активными сессиями.

Скриншоты складываются в стандартную папку KDE для снимков и дублируются в
`screenshots/` рядом с сервером — помни, что они могут содержать чувствительные
данные с экрана.

## Тесты

Тесты живые: они открывают настоящие окна и снимают настоящий экран, поэтому
требуют запущенной сессии KDE Plasma.

```bash
pip install -r requirements-dev.txt
python3 tests/test_bridge.py      # pty-мост: команды, вывод, код возврата
python3 tests/test_interactive.py  # ввод в программу, ctrl+c, история
python3 tests/test_e2e.py          # полный цикл: окно → команды → фокус → снимок
python3 tests/test_mcp_stdio.py    # протокол MCP: список тулов и вызовы
```

В CI (`.github/workflows/ci.yml`) гоняются только линтер и проверка импорта —
живые тесты в контейнере без композитора не запустятся.

## Лицензия

MIT — см. [LICENSE](LICENSE).