Screen Control
Fornece uso seguro de computador para agentes de IA no Windows: percepção de tela via OCR, quadros individuais e um stream MJPEG ao vivo, além de controle de mouse, teclado, janela, área de trabalho virtual e modo de jogo com portas de segurança, por meio de uma API HTTP local autenticada por token e MCP (16 ferramentas).
Documentação
🖥️ Screen Control
Um sistema de controle remoto local para agentes de IA: observe a tela do seu computador ao vivo e envie comandos de mouse/teclado para ela. Tudo roda na sua própria máquina — nenhum dado sai dela, sem intermediário na nuvem.
Cada tecla pressionada e cada captura de tela neste GIF passaram pela API — o agente nunca tocou em um teclado físico.
Por que Screen Control?
Agentes de IA hoje conseguem escrever código e chamar APIs — mas não conseguem ver ou tocar sua área de trabalho. O Screen Control dá a qualquer agente o uso geral do computador por meio de uma interface HTTP/MCP limpa e protegida por segurança:
- Perceber — OCR para texto, quadros individuais ou um fluxo MJPEG ao vivo para modelos com capacidade de visão, e um endpoint de diff somente texto para modelos que não conseguem consumir imagens.
- Agir — mouse absoluto e relativo, teclado seguro para Unicode, gerenciamento de janelas, controle em segundo plano (sem foco), áreas de trabalho virtuais.
- Manter a segurança — autenticação por token, atalhos perigosos bloqueados, proteção de foco, um watchdog para entrada travada e um failsafe de emergência são todos aplicados no lado do servidor, não importa o quão confuso o agente fique.
Um único processo, zero configuração, funciona com qualquer linguagem que fale HTTP — ou nativamente via MCP no Claude Desktop, Cursor, VS Code e agentes em nuvem.
O Desempenho É Limitado pelo Agente
O Screen Control é a camada de percepção e atuação — os olhos e as mãos. A velocidade e a capacidade efetivas de qualquer agente que o utiliza são limitadas pelo próprio agente e pelo ambiente em que ele roda:
- Velocidade de raciocínio — uma ação por "turno" do agente: o loop perceber → planejar → agir → verificar vive no agente, então a latência de inferência do modelo e a profundidade do raciocínio definem diretamente o ritmo. A API em si adiciona apenas milissegundos por chamada.
- Capacidade de contexto — leituras de tela (texto OCR, quadros, diffs) consomem a janela de contexto do agente; uma janela maior significa mais consciência situacional antes que a verificação se degrade.
- Ambiente de execução — latência de rede, sobrecarga de ida e volta MCP/HTTP, limites de chamadas de ferramentas e restrições de hospedagem se acumulam sobre o loop.
Na prática, isso significa: o mesmo repositório torna um modelo de raciocínio rápido rápido e capaz, e torna um modelo lento lento — a cadeia de ferramentas não é o gargalo. Tarefas em tempo real ou com muitas ações precisam de um agente com inferência rápida e latência de loop de ferramentas baixa; agentes mais lentos devem preferir tarefas deliberadas, com muita verificação.
Sumário
- Por que Screen Control?
- Recursos
- Arquitetura
- Instalação
- Início Rápido
- Referência da API
- 🤖 Para Agentes de IA
- Suporte MCP (Agentes em Nuvem com Um Clique)
- Modelo de Segurança
- Guia do Modo Jogo
- Guia de Acesso à Visão
- Solução de Problemas
- Testes
- Contribuindo
- Licença
Recursos
| Recurso | Descrição |
|---|---|
| 🖼️ Feed de tela ao vivo | Captura de tela atualizando continuamente no navegador |
| 🖱️ Controle do mouse | Clique, clique direito, clique duplo, rolagem, arrastar e soltar via captura de tela ao vivo |
| ⌨️ Controle do teclado | Digitação de texto (inclui Unicode/Turco, independente de layout), teclas e atalhos (Ctrl+C, Alt+Tab…) |
| 👁️ OCR | Converte texto na tela para formato legível por máquina |
| 📷 Acesso à visão | Caminhos de pixels brutos para modelos com capacidade de imagem: quadros individuais, fluxo MJPEG, detecção de movimento baseada em texto |
| 🪟 Gerenciamento de janelas | Listar, focar, fechar com segurança (WM_CLOSE), encerrar (estilo gerenciador de tarefas) |
| 🖥️ Controle sem foco | Ler/escrever janelas em segundo plano via PostMessage sem roubar o foco |
| 🎮 Modo jogo | Olhar da câmera via movimento relativo do mouse, teclas de segurar para mover |
| 🔐 Autenticação por token | Toda solicitação exige X-Auth-Token (proteção CSRF) |
| 🦺 Watchdog de entrada travada | Libera automaticamente teclas pressionadas após 30 s de inatividade |
| 🛟 Failsafe | Cursor no canto superior esquerdo aborta todos os comandos (desativado no modo jogo) |
Arquitetura
┌─────────────────────────────────────────────────────────┐
│ Browser (Web UI) │
│ ┌──────────┐ ┌──────────┐ ┌────────────────────────┐ │
│ │ Live │ │ Control │ │ Windows / Game Mode │ │
│ │ View │ │ Panel │ │ Panel │ │
│ └────┬─────┘ └────┬─────┘ └───────────┬────────────┘ │
│ │ │ │ │
└───────┼──────────────┼─────────────────────┼──────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────┐
│ HTTP API (Flask) │
│ 127.0.0.1:8745 │
│ │
│ /api/screenshot /api/mouse /api/key │
│ /api/vision/* /api/ocr /api/window │
│ /api/game /api/held /api/release_all │
│ /api/windows /api/desktops /api/desktop │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Auth Layer │ │ Watchdog │ │ OCR Engine │ │
│ │ (token) │ │ (30s auto) │ │ (RapidOCR) │ │
│ └─────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────┐
│ control.py (Core) │
│ │
│ Screen: mss (fast capture), PIL (processing) │
│ Mouse: pyautogui (absolute), SendInput (relative) │
│ Keyboard: pyautogui + SendInput+KEYEVENTF_UNICODE │
│ Windows: Win32 API (EnumWindows, SetForegroundWindow) │
│ Background: PrintWindow (capture), PostMessage (input) │
│ Virtual Desktops: pyvda │
│ Game Mode: ClipCursor + MOUSE_MOVE_RELATIVE │
└─────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────┐
│ backends/ (pluggable) │
│ WindowsBackend │ LinuxBackend │ MacOSBackend│
│ (full) │ (stub) │ (stub) │
└──────────────────────────────────────────────┘
Coordenadas e Concorrência
Consciência de DPI por monitor. control.py chama
SetProcessDpiAwarenessContext(PER_MONITOR_AWARE_V2) no momento da importação —
antes da importação de pyautogui, porque o pyautogui toca nas APIs de coordenadas
durante a importação e, caso contrário, bloquearia o processo no padrão do manifesto do
interpretador (ciente do sistema). Com PMv2 ativo, cada coordenada no sistema é um
pixel físico de ponta a ponta: captura mss, caixas delimitadoras de OCR,
cliques pyautogui/SendInput, ClipCursor. Em displays de alto DPI (escala de 125%/150%),
nada se desvia entre o que o OCR relata e onde o mouse clica.
Arquitetura de locks. O servidor usa dois locks independentes em vez de um lock global:
| Lock | Protege | Endpoints |
|---|---|---|
_input_lock | mouse, teclado, modo jogo, operações de janela | /api/mouse, /api/key, /api/game, /api/window/post, ... |
_read_lock | captura, OCR, visão, enumeração | /api/screenshot, /api/ocr, /api/vision/*, /api/windows, ... |
Um OCR lento (3–5 s em uma tela ocupada) não congela mais leituras simultâneas de captura ou visão — leituras ficam na fila atrás de leituras, entradas atrás de entradas.
Princípio de Funcionamento do Loop ao Vivo
Este sistema é projetado para um loop ao vivo de perceber-agir, não cadeias de comandos pré-escritas:
- LER — OCR ou visão lê a tela antes e depois de cada ação
- UMA AÇÃO — cada rodada envia um único comando
- VERIFICAR — o critério de aceitação é "apareceu na tela", não "eu enviei"
- ADAPTAR — se a verificação falhar, o próximo passo muda com base no que é realmente visto
Isso é aplicado pela proteção expect_hwnd: a digitação é recusada (409) se
a janela em primeiro plano não corresponder ao alvo.
Instalação
cd screen-control
pip install -r requirements.txt
Requisitos
| Pacote | Finalidade | Obrigatório? |
|---|---|---|
mss | Captura de tela rápida | ✅ Sim |
pyautogui | Controle de mouse/teclado | ✅ Sim |
pyvda | Gerenciamento de áreas de trabalho virtuais | ✅ Sim |
flask | Servidor HTTP | ✅ Sim |
Pillow | Processamento de imagem | ✅ Sim |
rapidocr-onnxruntime | OCR (leitura de texto na tela) | ⚠️ Opcional |
Nota: O pacote de OCR é grande e pode demorar para instalar. Se ele falhar, todo o resto ainda funciona — apenas o recurso de OCR fica indisponível.
Requisitos do Sistema
- SO: Windows 10/11 (x64)
- Python: 3.10+
- Display: Qualquer resolução; o sistema se adapta automaticamente
Início Rápido
# 1. Start the server
cd screen-control
python server.py
# 2. Open in browser
# http://127.0.0.1:8745
# 3. Or control via API
TOKEN=$(cat .token)
curl -H "X-Auth-Token: $TOKEN" http://127.0.0.1:8745/api/screenshot -o screen.jpg
Referência da API
Capacidades
Retorna o nome do backend ativo e o que ele pode fazer. Os agentes devem chamar isso primeiro (veja ROADMAP.md para o plano multiplataforma).
GET /api/capabilities
→ {"ok": true, "backend": "windows",
"capabilities": {"screen_capture": true, "game_mode": true, ...}}
Valores de recursos: true (suportado), false (ausente), null (desconhecido —
backend stub), "optional" (depende de uma dependência opcional).
Matriz de Suporte de Plataformas
| Capacidade | Windows | Linux X11 | Linux Wayland | macOS |
|---|---|---|---|---|
| Captura de tela | Completa | Completa | Dependente de portal | Permissão necessária |
| OCR | Completa/opcional | Completa/opcional | Completa/opcional | Completa/opcional |
| Controle do mouse | Completo | Completo | Restrito | Permissão de acessibilidade |
| Controle do teclado | Completo | Completo | Restrito | Permissão de acessibilidade |
| Enumeração de janelas | Completa | Dependente de WM | Limitada | Dependente de acessibilidade/API |
| Entrada em segundo plano | Forte | Dependente de WM/aplicativo | Geralmente indisponível | Limitada |
| Áreas de trabalho virtuais | Suportado | Dependente de DE/WM | Dependente de DE/WM | Específico do Spaces |
| Modo jogo | Suportado | Experimental | Limitado | Experimental |
Os backends Linux e macOS são atualmente stubs com falha fechada: toda operação retorna
BACKEND_UNAVAILABLE(501) até ser implementada (Fases 5–7 do ROADMAP). O Windows é o backend de referência.
Autenticação
Toda solicitação deve incluir o cabeçalho X-Auth-Token. O token é
gerado a cada início do servidor e gravado em .token.
TOKEN=$(cat .token)
| Código | Significado |
|---|---|
| 401 | Token ausente ou inválido |
| 415 | POST sem Content-Type: application/json |
Bootstrap do token (para a interface web integrada):
GET /token
→ {"ok": true, "token": "abc123..."}
O endpoint
/tokené seguro: a Política de Mesma Origem impede que páginas estrangeiras o leiam.
Captura de Tela
GET /api/screenshot
Retorna uma captura de tela em JPEG.
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
monitor | int | 1 | Índice do monitor |
region | string | — | Sub-região x,y,w,h |
curl -H "X-Auth-Token: $TOKEN" -o screen.jpg http://127.0.0.1:8745/api/screenshot
curl -H "X-Auth-Token: $TOKEN" "http://127.0.0.1:8745/api/screenshot?region=0,0,800,600"
GET /api/info
Retorna as dimensões da tela e o estado do sistema.
{"ok": true, "width": 1920, "height": 1080, "ocr_available": true,
"failsafe": true, "game_mode": false}
Controle do Mouse
POST /api/mouse
| action | Parâmetros obrigatórios | Parâmetros opcionais | Descrição |
|---|---|---|---|
move | x, y | duration (padrão 0.15) | Move o cursor para posição absoluta |
click | x, y | button (esquerdo/direito), clicks (padrão 1) | Clica na posição |
scroll | clicks | x, y | Roda a roda do mouse (positivo=para cima) |
drag | x1, y1, x, y | duration, button | Arrasta entre dois pontos |
down | button (padrão "esquerdo") | — | Pressiona e segura o botão do mouse |
up | button (padrão "esquerdo") | — | Solta o botão do mouse pressionado |
# Click at center of screen
curl -X POST http://127.0.0.1:8745/api/mouse -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"action":"click","x":960,"y":540,"button":"left"}'
# Right-click
curl -X POST http://127.0.0.1:8745/api/mouse -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"action":"click","button":"right","x":960,"y":540}'
# Scroll down
curl -X POST http://127.0.0.1:8745/api/mouse -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"action":"scroll","clicks":-3}'
Controle do Teclado
POST /api/key
| action | Parâmetros obrigatórios | Descrição |
|---|---|---|
press | key | Pressiona e solta uma tecla |
down | key | Segura uma tecla pressionada (rastreada pelo watchdog) |
up | key | Solta uma tecla pressionada |
hotkey | keys (array) | Combinação de teclas (ex.: ["ctrl","c"]) |
type | text | Digita texto (Unicode, independente de layout) |
| Parâmetro opcional | Padrão | Descrição |
|---|---|---|
expect_hwnd | — | Identificador da janela para verificar o foco (409 se houver incompatibilidade) |
interval | 0.03 | Atraso entre caracteres para type |
# Press Enter
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"press","key":"enter"}'
# Ctrl+C
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"hotkey","keys":["ctrl","c"]}'
# Type text (Turkish characters supported)
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"type","text":"Merhaba dünya"}'
# Hold W key down (for walking in games)
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"down","key":"w"}'
OCR (Leitura de Tela)
POST /api/ocr
Converte texto na tela para formato legível por máquina.
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
region | array | — | Sub-região [x, y, w, h] (mais rápido) |
{
"ok": true,
"text": "Hello World\nFile Edit View",
"lines": ["Hello World", "File Edit View"],
"items": [
{"text": "Hello World", "x": 960, "y": 40},
{"text": "File Edit View", "x": 100, "y": 15}
]
}
# Full screen OCR
curl -X POST http://127.0.0.1:8745/api/ocr -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{}'
# Region-only (faster, ~10x for small regions)
curl -X POST http://127.0.0.1:8745/api/ocr -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"region":[0,0,800,100]}'
Acesso à Visão (Modelos de Imagem)
Três endpoints para modelos que podem consumir imagens:
| Endpoint | Descrição |
|---|---|
GET /api/vision/frame | Quadro JPEG individual (bruto ou base64) |
GET /api/stream | Fluxo ao vivo MJPEG |
POST /api/vision/diff | Detecção de movimento baseada em texto (sem necessidade de visão) |
GET /api/vision/frame
| Parâmetro | Padrão | Descrição |
|---|---|---|
scale | 1.0 | Fator de redução (0.5 = metade do tamanho) |
gray | 0 | 1 para escala de cinza |
quality | 80 | Qualidade JPEG (20-95) |
format | — | base64 para resposta JSON |
region | — | x,y,w,h sub-região |
# Half-size greyscale frame as base64 (for text-only models)
curl "http://127.0.0.1:8745/api/vision/frame?scale=0.5&gray=1&format=base64" \
-H "X-Auth-Token: $TOKEN"
GET /api/stream
Fluxo ao vivo MJPEG. Coloque em <img src> ou consuma quadro a quadro.
| Parâmetro | Padrão | Descrição |
|---|---|---|
fps | 10 | Quadros por segundo (1-30) |
quality | 70 | Qualidade JPEG |
scale | 1.0 | Fator de redução |
region | — | x,y,w,h sub-região |
POST /api/vision/diff
Detecção de movimento baseada em texto — nenhum modelo de visão necessário.
| Corpo | Descrição |
|---|---|
{} | Comparar com o último quadro armazenado |
{"grab":"gray"} | Armazenar o quadro atual para a próxima comparação |
{"b64_prev":"..."} | Comparar com o quadro anterior fornecido |
{
"ok": true,
"changed": true,
"changed_pct": 12.5,
"bbox": [100, 200, 400, 350],
"tiles": [
{"row": 2, "col": 4, "pct": 35.2, "center": [1000, 390]}
]
}
Gerenciamento de Janelas
GET /api/windows
Lista todas as janelas visíveis.
{
"ok": true,
"windows": [
{
"hwnd": 123456,
"title": "My Application",
"process": "app.exe",
"pid": 7890,
"focused": true,
"rect": [0, 0, 1920, 1080],
"desktop": 1
}
]
}
POST /api/window
| ação | Obrigatório | Opcional | Descrição |
|---|---|---|---|
focus | hwnd | — | Trazer janela para o primeiro plano |
close | hwnd | expect_title, expect_process | Fechamento seguro via WM_CLOSE |
kill | hwnd, pid | — | Forçar encerramento (estilo gerenciador de tarefas) |
topmost | hwnd | — | Definir sempre no topo |
untopmost | hwnd | — | Remover sempre no topo |
maximize | hwnd | — | Maximizar janela |
# Focus a window
curl -X POST http://127.0.0.1:8745/api/window -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"hwnd":12345,"action":"focus"}'
# Safe close (with title verification)
curl -X POST http://127.0.0.1:8745/api/window -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"action":"close","hwnd":12345,"expect_title":"Notepad"}'
# Kill process
curl -X POST http://127.0.0.1:8745/api/window -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"kill","hwnd":12345,"pid":7890}'
Controle Sem Foco (Em Segundo Plano)
Leia e controle janelas sem roubar o foco — o usuário continua trabalhando em sua área de trabalho principal.
GET /api/window/capture
Captura uma janela via PrintWindow (funciona mesmo em outra área de trabalho virtual).
| Parâmetro | Descrição |
|---|---|
hwnd (obrigatório) | Identificador da janela |
client | 1 = apenas área do cliente |
ocr | 1 = retornar texto OCR em vez de imagem |
# Capture window as PNG
curl "http://127.0.0.1:8745/api/window/capture?hwnd=12345" \
-H "X-Auth-Token: $TOKEN" -o window.png
# Capture + OCR in one call
curl "http://127.0.0.1:8745/api/window/capture?hwnd=12345&ocr=1" \
-H "X-Auth-Token: $TOKEN"
POST /api/window/post
Envia entrada para uma janela, escolhendo o caminho de entrega automaticamente.
| ação | Descrição |
|---|---|
type | Digitar texto (compatível com Unicode) |
key | Enviar um pressionamento de tecla |
hotkey | Enviar uma combinação de teclas |
click | Clicar nas coordenadas do cliente |
scroll | Rolar a janela |
drag | Arrastar dentro da janela |
O parâmetro opcional mode controla o roteamento:
| modo | Comportamento |
|---|---|
auto (padrão) | Decidido pela sonda de modo de entrada (veja abaixo) |
background | Forçar caminho PostMessage (janela mantém foco/ordem-z) |
focused | Forçar caminho de foco + SendInput |
# Type into a background Notepad
curl -X POST http://127.0.0.1:8745/api/window/post -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"hwnd":12345,"action":"type","text":"Hello from background!"}'
Regras de roteamento (mode=auto):
postmessage— aplicativo Win32 clássico: PostMessage em segundo plano, sem mudança de foco.uia— superfície WinUI/UWP/XAML (canvas DirectX único, sem controles filhos Win32): mensagens postadas são silenciosamente engolidas, então a janela é focada e a ação é reproduzida via SendInput (coordenadas do cliente convertidas para tela). Este é o fallback documentado para aplicativos modernos.focused— janela já está em primeiro plano: caminho SendInput com foco.invalid— HTTP 409; não é uma janela de nível superior acessível.
GET /api/window/input-mode
Classifica como uma janela recebe entrada antes de postar nela. Retorna um de
focused | postmessage | uia | invalid.
curl "http://127.0.0.1:8745/api/window/input-mode?hwnd=12345" \
-H "X-Auth-Token: $TOKEN"
Nota WinUI: O novo Bloco de Notas (e outros aplicativos hospedados em XAML) não tem controle filho Edit clássico para postar — toda a interface é uma superfície DirectX.
input-moderelatauiapara estes;/api/window/postentão automaticamente usa o caminho SendInput com foco./api/window/childrenpermanece útil para aplicativos clássicos com controles filhos reais.
GET /api/window/children
Lista controles filhos de uma janela (nome da classe + título + hwnd).
curl "http://127.0.0.1:8745/api/window/children?hwnd=12345" -H "X-Auth-Token: $TOKEN"
Áreas de Trabalho Virtuais
GET /api/desktops
Lista todas as áreas de trabalho virtuais.
POST /api/desktop
| ação | Parâmetros | Descrição |
|---|---|---|
switch | number | Alternar para a área de trabalho N |
create | — | Criar uma nova área de trabalho |
curl http://127.0.0.1:8745/api/desktops -H "X-Auth-Token: $TOKEN"
curl -X POST http://127.0.0.1:8745/api/desktop -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"switch","number":2}'
Modo Jogo
| ação | Parâmetros | Descrição |
|---|---|---|
start | sensitivity (padrão 12) | Travar cursor no centro, habilitar entrada de jogo |
move | dx, dy, sensitivity | Rotacionar câmera (mouse relativo) |
stop | — | Liberar cursor + toda entrada pressionada |
heartbeat | — | Manter vivo para pressionamentos longos |
# Start game mode
curl -X POST http://127.0.0.1:8745/api/game -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"start","sensitivity":12}'
# Look right
curl -X POST http://127.0.0.1:8745/api/game -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"move","dx":50,"dy":0}'
# Hold W to walk forward
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"down","key":"w"}'
# ... later ...
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"up","key":"w"}'
# Stop game mode
curl -X POST http://127.0.0.1:8745/api/game -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"stop"}'
Endpoints de Segurança
GET /api/held
Retorna teclas/botões atualmente pressionados e status do watchdog.
{
"ok": true,
"keys": ["w", "shift"],
"buttons": ["left"],
"game_mode": true,
"idle_seconds": 5.2,
"watchdog_count": 0,
"last_watchdog": null
}
POST /api/release_all
Emergência: liberar tudo (teclas pressionadas, botões do mouse, trava de cursor do modo jogo).
curl -X POST http://127.0.0.1:8745/api/release_all -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{}'
🤖 Para Agentes de IA
Um guia dedicado e abrangente para agentes de IA (LLMs, modelos de visão, estruturas de automação) está disponível em AGENT_GUIDE.md.
Ele cobre:
- Loop perceber-agir (ler → planejar → agir → verificar)
- Guarda de foco (
expect_hwnd) para prevenir acidentes com janela errada - Fluxos de trabalho de automação de aplicativos e controle de jogos
- Acesso à visão para modelos capazes de imagem
- Detecção de movimento baseada em texto
- Otimização de largura de banda
- Exemplos completos de curl
Suporte MCP (Agentes de Nuvem com Um Clique)
Model Context Protocol (MCP) transforma este projeto em uma caixa de ferramentas plug-and-play para qualquer agente compatível com MCP: Claude Desktop, Claude Code, Cursor, modo Agente do VS Code Copilot, agentes de nuvem personalizados — sem código de cola personalizado, sem scripts curl. O agente descobre e chama as ferramentas nativamente.
Como funciona
MCP agent (cloud or desktop)
│ MCP protocol (stdio or streamable-HTTP)
▼
mcp_server.py ← thin wrapper: tools → HTTP calls, token auto-read
│ REST + X-Auth-Token (localhost only)
▼
server.py ← the single source of truth:
auth, locks, watchdog, focus guard, all safety rules
mcp_server.py adiciona nenhum novo poder — todo mecanismo de segurança
(token de autenticação, travas de entrada/leitura, watchdog, bloqueio Alt+F4, guarda de foco,
failsafe) permanece aplicado por server.py.
Configuração
pip install mcp # optional dependency (see requirements.txt)
python server.py # start the REST server first (it writes .token)
O servidor MCP lê automaticamente o token de .token (ou a
variável de ambiente SCREEN_CONTROL_TOKEN) — configuração zero.
Agentes de desktop (transporte stdio)
Claude Desktop — claude_desktop_config.json:
{
"mcpServers": {
"screen-control": {
"command": "python",
"args": ["C:/path/to/screen-control/mcp_server.py"]
}
}
}
Claude Code: claude mcp add screen-control -- python C:/path/to/screen-control/mcp_server.py
Cursor / VS Code: adicione a mesma entrada aos seus arquivos de configuração MCP.
Agentes remotos / de nuvem (transporte streamable-HTTP)
python mcp_server.py --http --port 8751
# MCP endpoint: http://127.0.0.1:8751/mcp
O transporte HTTP é protegido por token: toda solicitação deve carregar o
cabeçalho X-Auth-Token (mesmo token do servidor REST). Tokens na string de consulta
(?token=...) são rejeitados por design — URLs vazam em logs de proxy/túnel,
histórico do navegador e links compartilhados, e este token concede controle total do
desktop. Clientes que não podem enviar cabeçalhos personalizados devem executar um stdio
local mcp_server.py em vez disso. Apenas GET /health está aberto, para sondas de
liveness. A proteção contra DNS-rebinding está desabilitada neste transporte deliberadamente —
solicitações via túnel chegam com um cabeçalho Host estrangeiro, e a ameaça de
rebinding já é coberta pela proteção do token.
Para um agente de nuvem, exponha-o através de um túnel:
cloudflared tunnel --url http://127.0.0.1:8751
# → prints a https://<random>.trycloudflare.com URL
Em seguida, configure a conexão MCP do agente com <tunnel-url>/mcp mais o
token de .token como cabeçalho (X-Auth-Token).
Conectores sem cabeçalho (chaves com escopo)
Para clientes que não podem enviar cabeçalhos personalizados (ex.: conectores web que apenas aceitam uma URL de endpoint), crie uma chave de API com escopo — uma credencial persistente, opcionalmente com limite de tempo — e incorpore-a no caminho da URL:
# Create a 24-hour scoped key (requires the server to be running)
curl -X POST http://127.0.0.1:8745/api/keys -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"action":"create","name":"spark","expires_in_hours":24}'
# Connector endpoint becomes:
# https://<tunnel-url>/mcp/<scoped-key>
Garantias de design (SC-06):
- O token de sessão mestre é recusado em URLs (403) — apenas chaves com escopo podem viajar lá
- Chaves com escopo expiram automaticamente; chaves expiradas não autenticam nada
- Chaves com escopo são revogáveis por nome a qualquer momento via
POST /api/keys({"action":"revoke","name":"spark"}) — a revogação tem efeito imediato em todos os endpoints - O arquivo
.apikeysarmazena apenas hashes SHA-256, nunca chaves brutas
⚠️ Um túnel expõe o controle do PC à internet. Mantenha o token em segredo, prefira túneis de curta duração e chaves com escopo para conectores sem cabeçalho, e pare o servidor quando não estiver em uso.
Inicialização com Um Comando (launcher + túnel automático)
start-server.bat automatiza toda a configuração de nuvem e imprime tudo
que seu agente de nuvem precisa, pronto para colar:
- Baixa
cloudflared.exese ausente (portátil, sem necessidade de admin) - Para instâncias remanescentes de uma execução anterior
- Inicia o servidor REST (porta 8745) e o servidor HTTP MCP (porta 8751)
- Aguarda até que ambos estejam saudáveis (sondas
/tokene/health) - Inicia um túnel rápido cloudflared, extrai sua URL pública de
tunnel.log, e imprime o resumo:
============================================================
ALL SYSTEMS RUNNING
============================================================
Local REST API : http://127.0.0.1:8745
Local MCP : http://127.0.0.1:8751/mcp
Public MCP URL : https://<random>.trycloudflare.com/mcp
------------------------------------------------------------
PASTE INTO YOUR CLOUD AGENT (MCP connector settings)
------------------------------------------------------------
Endpoint : https://<random>.trycloudflare.com/mcp
Header : X-Auth-Token: <token>
URL form : https://<random>.trycloudflare.com/mcp?token=<token>
(only if the connector cannot send headers)
------------------------------------------------------------
stop-server.bat para todos os três (REST, MCP, túnel) de uma só vez.
Ferramentas disponíveis (16)
| Categoria | Ferramentas |
|---|---|
| Percepção | get_info, ocr_screen, screenshot (bloco de imagem real para modelos de visão), motion_diff |
| Mouse / teclado | mouse, keyboard (com expect_hwnd), get_held, release_all |
| Janelas | list_windows, focus_window, window_children, window_input_mode, window_post, window_capture_ocr, close_window |
| Modo jogo | game (iniciar / mover / parar / heartbeat) |
Qual transporte para quem
| Consumidor | Transporte | Comando |
|---|---|---|
| Claude Desktop / Cursor / VS Code (local) | stdio | python mcp_server.py |
| Claude Code | stdio | claude mcp add ... (acima) |
| Agentes de nuvem / remotos | streamable-HTTP | start-server.bat (recomendado) ou python mcp_server.py --http --port 8751 + cloudflared tunnel --url http://127.0.0.1:8751 |
Nota: Este projeto tem como alvo o MCP Python SDK 2.x (API
MCPServer). Com SDK 1.x, substitua o import porfrom mcp.server.fastmcp import FastMCP, ImageeMCPServerporFastMCP.
Modelo de Segurança
Ameaça: Páginas Web Maliciosas (CSRF)
Mesmo vinculado a 127.0.0.1, uma página maliciosa no navegador pode acionar
solicitações sem pré-voo (fetch text/plain, POST de formulário HTML) para localhost.
O navegador bloqueia a resposta mas não a solicitação — o servidor
ainda executaria o comando.
Mitigação: Toda solicitação requer X-Auth-Token. Uma página estrangeira
não pode ler este token (Política de Mesma Origem), então não pode autenticar.
Camadas adicionais:
- Solicitações POST devem usar
Content-Type: application/json(415 caso contrário) - Isso bloqueia POSTs codificados em formulário e text-plain mesmo se o token vazar
- Confiança no cabeçalho Host (DNS rebinding): quando vinculado a loopback, solicitações
carregando um cabeçalho
Hostnão-loopback são recusadas com421— uma página de rebinding que resolve seu domínio para127.0.0.1não pode ler/tokenou chamar a API - Respostas
/tokene/carregamCache-Control: no-storepara que a credencial nunca seja persistida por navegadores ou proxies
Ameaça: Teclas Presas / Trava do Modo Jogo
No modo jogo, ClipCursor fixa o cursor em uma caixa 2×2 — o clássico
failsafe do pyautogui (cursor no canto superior esquerdo) não funciona.
Mitigações:
Esc/Alt+Tabfísico — entrada de hardware real; esta API não pode bloqueá-lo, e sempre funcionaPOST /api/release_all— liberação instantânea de tudo- Watchdog (automático) — 30 s de inatividade no lado do servidor com entrada pressionada aciona liberação automática
Ameaça: Digitação na Janela Errada
Mitigações:
- Guarda
expect_hwndem/api/key— se a janela em primeiro plano não corresponder, a digitação é recusada com 409 - O caminho focado de
/api/window/postverifica o foco após a troca de foco e antes de qualquer entrada sintética (409 em incompatibilidade) — a entrada nunca é reproduzida em qualquer janela que esteja em primeiro plano focus_window()levanta erro em falha em vez de retornar silenciosamente
Ameaça: Combinações de Teclas Perigosas
Mitigação: Bloqueado no nível da API (403) em todos os caminhos de entrega —
a rota direta /api/key, a rota de /api/window/post em segundo plano
(PostMessage) e a rota de fallback focada compartilham uma única política de
segurança (control._assert_allowed):
Alt+F4— a única combinação Alt proibida (Alt+Tab, Alt+menu são legítimos)- Tecla Win — impede o menu Iniciar, alternância de tarefas
Ctrl+Alt+Del— tela de segurança do sistema- Estilo
Shift+Delete— impede exclusão permanente
Ameaça: Matando Processos do Sistema
Mitigações:
- O nome do processo é resolvido diretamente do PID (snapshot toolhelp do Win32), não do inventário de janelas visíveis — processos de sistema em segundo plano/sem janela recebem a mesma proteção que os visíveis
- Processos críticos do sistema estão na lista negra (negação padrão para PIDs
desconhecidos):
winlogon.exe,csrss.exe,smss.exe,services.exe,lsass.exe,svchost.exe,system,registry,dwm.exe - Confirmação opcional de
expect_process: uma incompatibilidade aborta o kill com409— protege contra matar um PID recém-reutilizado
Ameaça: Exaustão de Recursos (agente malicioso / DoS)
Um cliente que possui token, mas se comporta mal, não deve ser capaz de esgotar a memória ou privar o bloqueio de entrada.
Mitigações:
MAX_CONTENT_LENGTH= 1 MB — corpos de requisição superdimensionados são rejeitados (413)- Largura/altura/área de
regionescalesão limitadas (400 caso contrário) - Payloads de
textsão limitados a 10.000 caracteres por chamada de entrada - Streams MJPEG são limitados a 10 clientes simultâneos (429 além disso)
- O limite de bomba de descompressão do Pillow está definido para imagens fornecidas pelo cliente
Acesso à Rede
O servidor vincula-se a 127.0.0.1 por padrão. Para expô-lo à rede:
python server.py --host 0.0.0.0 # ⚠️ anyone on the network can control this machine
Guia do Modo Jogo
Configuração
# 1. Focus the game window
curl -X POST http://127.0.0.1:8745/api/window -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"hwnd":GAME_HWND,"action":"focus"}'
# 2. Start game mode
curl -X POST http://127.0.0.1:8745/api/game -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"start","sensitivity":12}'
Olhar da Câmera
# Look right
curl -X POST http://127.0.0.1:8745/api/game -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"move","dx":50,"dy":0}'
# Look down
curl -X POST http://127.0.0.1:8745/api/game -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"move","dx":0,"dy":30}'
Movimento
# Walk forward (hold W)
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"down","key":"w"}'
# ... walk for a while ...
# Release W
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"up","key":"w"}'
Específico do Minecraft
# Place block (right-click)
curl -X POST http://127.0.0.1:8745/api/mouse -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{"action":"click","button":"right","x":960,"y":540}'
# Break block (hold left-click)
curl -X POST http://127.0.0.1:8745/api/mouse -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"down","button":"left"}'
# ... after breaking ...
curl -X POST http://127.0.0.1:8745/api/mouse -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"up","button":"left"}'
# Select hotbar slot
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"press","key":"1"}'
# Open inventory
curl -X POST http://127.0.0.1:8745/api/key -H "X-Auth-Token: $TOKEN" \
-H "Content-Type: application/json" -d '{"action":"press","key":"e"}'
Adequação
| Tipo de Jogo | Adequado? | Notas |
|---|---|---|
| Minecraft (construção) | ✅ Sim | Colocar blocos, andar, minerar |
| Minecraft (PvP) | ❌ Não | Muito lento para combate rápido |
| Jogos de turno | ✅ Sim | Tempo amplo para ler→agir→verificar |
| RPG / aventura | ✅ Sim | Inventário, diálogo, exploração |
| FPS rápido | ❌ Não | Tempo de reação insuficiente |
| Jogos de quebra-cabeça | ✅ Sim | Baseado em cliques, leitura intensa |
Guia de Acesso à Visão
Para Modelos com Capacidade de Imagem
Se o modelo consumidor pode processar imagens, use os endpoints de visão diretamente:
GET /api/vision/frame?scale=0.5&gray=1&quality=70
Isso retorna um único JPEG que o modelo pode analisar para:
- Elementos de HUD do jogo (vida, mana, inventário)
- Texto na tela (menus, chat, dicas)
- Compreensão visual da cena (blocos, entidades, terreno)
Para Modelos Somente Texto
Use o endpoint de diff para detecção de movimento sem visão:
POST /api/vision/diff {"grab":"gray"} → first call: stores frame
POST /api/vision/diff → subsequent calls: returns diff
A resposta informa onde as coisas mudaram (coordenadas de tile) e quanto (porcentagem), o que é suficiente para:
- Detectar que uma ação teve efeito
- Localizar elementos em movimento na tela
- Rastrear mudanças de estado de animação
Otimização de Largura de Banda
| Abordagem | Payload | Caso de Uso |
|---|---|---|
scale=1.0, gray=0 | ~500 KB | Detalhe completo |
scale=0.5, gray=1 | ~50 KB | Bom para a maioria dos modelos de visão |
scale=0.25, gray=1 | ~10 KB | Compressão máxima |
diff (texto) | ~1 KB | Agentes somente texto |
region=... | Variável | Foco em área específica |
Solução de Problemas
"Mecanismo de OCR não instalado"
pip install rapidocr-onnxruntime
Servidor não inicia (porta em uso)
# Find the process using port 8745
netstat -ano | findstr ":8745"
# Kill it
taskkill /PID <pid> /F
"Incompatibilidade de foco" (409) ao digitar
A janela em primeiro plano mudou entre a chamada de foco e a chamada de digitação.
Solução: sempre passe expect_hwnd e verifique o foco antes de digitar.
Janela não encontrada
A janela pode ter sido fechada ou pode ser uma janela do sistema que
EnumWindows não expõe. Tente:
curl http://127.0.0.1:8745/api/windows -H "X-Auth-Token: $TOKEN"
Cursor do modo jogo travado
Use POST /api/release_all ou pressione Esc / Alt+Tab fisicamente.
Alta latência de OCR
OCR em uma tela cheia de 1920×1080 pode levar de alguns segundos até ~30 s dependendo da sua CPU e da complexidade na tela. Use uma região — recortes pequenos são tipicamente 10× mais rápidos:
{"region": [0, 0, 800, 100]}
Caracteres turcos não aparecendo
O sistema usa SendInput + KEYEVENTF_UNICODE que é independente de layout.
Se os caracteres ainda não aparecerem, o aplicativo de destino pode não suportar
entrada Unicode — tente POST /api/window/post com action: "type" em vez disso.
Estrutura do Projeto
screen-control/
├── server.py # Flask HTTP server + all API endpoints
├── core/ # PlatformBackend interface + standardized errors
│ ├── backends.py # Abstract backend + lazy discovery
│ └── errors.py # ApiError envelope + error codes
├── backends/ # OS implementations behind PlatformBackend
│ ├── windows.py # Reference backend (moved from control.py)
│ ├── linux.py # Fail-closed stub (ROADMAP Phase 5)
│ ├── macos.py # Fail-closed stub (ROADMAP Phase 7)
│ ├── fake.py # In-memory backend for tests
│ └── forbidden.py # Shared blocked-key policy
├── control.py # Compatibility shim re-exporting backends.windows
├── tests/unit/ # Offline unit + integration tests (no real input)
├── mcp_server.py # MCP server (stdio + streamable-HTTP) — thin wrapper over the API
├── sdk/
│ └── screen_control.py # Python SDK client (pip-installable style)
├── index.html # Bundled web UI (live view + control panels)
├── docs/images/ # README assets (demo GIF captured by the API itself)
├── requirements.txt # Python dependencies
├── start-server.bat # One command: REST + MCP + cloud tunnel (Windows)
├── stop-server.bat # Stop all three processes
├── test-security.py # Security + game-mode test suite (34 checks)
├── test-game.py # Live game-mechanics test (app launch → draw → safe close)
├── test-endtoend.py # End-to-end test: open Notepad → type → save → verify
├── .github/workflows/ # CI: runs the security suite on every push
├── AGENT_GUIDE.md # AI agent integration guide (separate from this file)
├── README.md # This file
└── .token # Auto-generated auth token (gitignored)
Testes
Pré-requisitos
O servidor deve estar em execução:
cd screen-control
python server.py
Suíte de testes de segurança
Testa autenticação, combinações de teclas bloqueadas, gerenciamento de janelas, fechamento seguro, proteção de processos críticos e modo jogo — tudo não destrutivo.
cd screen-control
python test-security.py
Saída esperada:
== Token Authentication ==
✓ Missing token -> 401
✓ Wrong token -> 401
✓ Correct token -> 200
✓ Non-JSON POST -> 415
== Blocked Key Combos ==
✓ Alt+F4 blocked (403)
✓ Win key blocked (403)
✓ Win+D blocked (403)
✓ Delete blocked (403)
== Window List ==
✓ Windows list requires GET
✓ Window list is non-empty — 8 windows
✓ Exactly one focused window
== Safe Close Verification ==
✓ Wrong title aborts close
== Critical Process Protection ==
✓ System process (pid 4) rejected (403)
✓ pid 0 rejected (403)
== Game Mode ==
✓ Game mode started
✓ Relative camera look
✓ Game mode stopped
== Watchdog (dry run) ==
✓ Held state returns ok
✓ Watchdog count reported
========================================
RESULT: 19 passed, 0 failed
Teste ao vivo de mecânica de jogo
Inicia um aplicativo real (mspaint ou notepad), executa mecânicas de jogo de segurar-para-desenhar, verifica via análise de pixels e fecha com segurança com o tratamento do diálogo "Não Salvar".
cd screen-control
python test-game.py
Nota: Este teste inicia um aplicativo real. Ele lida com a limpeza automaticamente (envia WM_CLOSE e clica em "Não Salvar" se um diálogo aparecer).
Contribuindo
- Faça um fork do repositório
- Crie um branch de funcionalidade
- Faça suas alterações
- Teste em uma máquina Windows
- Envie um pull request
Estilo de Código
- Python: PEP 8, dicas de tipo, docstrings em todas as funções públicas
- Docstrings: Inglês, estilo Google
- Mensagens de erro: Inglês, descritivas
- Comentários: Inglês, explique porquê não o quê
Licença
Licença MIT. Consulte LICENSE para detalhes.
Construído com ❤️ para automação local e pesquisa de agentes de IA.