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

screen-control logo

License: MIT Platform Python CI: security tests MCP Listed on mcpservers.org

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.

An AI agent typing into Notepad through the screen-control API

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


Recursos

RecursoDescrição
🖼️ Feed de tela ao vivoCaptura de tela atualizando continuamente no navegador
🖱️ Controle do mouseClique, clique direito, clique duplo, rolagem, arrastar e soltar via captura de tela ao vivo
⌨️ Controle do tecladoDigitação de texto (inclui Unicode/Turco, independente de layout), teclas e atalhos (Ctrl+C, Alt+Tab…)
👁️ OCRConverte texto na tela para formato legível por máquina
📷 Acesso à visãoCaminhos de pixels brutos para modelos com capacidade de imagem: quadros individuais, fluxo MJPEG, detecção de movimento baseada em texto
🪟 Gerenciamento de janelasListar, focar, fechar com segurança (WM_CLOSE), encerrar (estilo gerenciador de tarefas)
🖥️ Controle sem focoLer/escrever janelas em segundo plano via PostMessage sem roubar o foco
🎮 Modo jogoOlhar da câmera via movimento relativo do mouse, teclas de segurar para mover
🔐 Autenticação por tokenToda solicitação exige X-Auth-Token (proteção CSRF)
🦺 Watchdog de entrada travadaLibera automaticamente teclas pressionadas após 30 s de inatividade
🛟 FailsafeCursor 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:

LockProtegeEndpoints
_input_lockmouse, teclado, modo jogo, operações de janela/api/mouse, /api/key, /api/game, /api/window/post, ...
_read_lockcaptura, 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:

  1. LER — OCR ou visão lê a tela antes e depois de cada ação
  2. UMA AÇÃO — cada rodada envia um único comando
  3. VERIFICAR — o critério de aceitação é "apareceu na tela", não "eu enviei"
  4. 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

PacoteFinalidadeObrigatório?
mssCaptura de tela rápida✅ Sim
pyautoguiControle de mouse/teclado✅ Sim
pyvdaGerenciamento de áreas de trabalho virtuais✅ Sim
flaskServidor HTTP✅ Sim
PillowProcessamento de imagem✅ Sim
rapidocr-onnxruntimeOCR (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

CapacidadeWindowsLinux X11Linux WaylandmacOS
Captura de telaCompletaCompletaDependente de portalPermissão necessária
OCRCompleta/opcionalCompleta/opcionalCompleta/opcionalCompleta/opcional
Controle do mouseCompletoCompletoRestritoPermissão de acessibilidade
Controle do tecladoCompletoCompletoRestritoPermissão de acessibilidade
Enumeração de janelasCompletaDependente de WMLimitadaDependente de acessibilidade/API
Entrada em segundo planoForteDependente de WM/aplicativoGeralmente indisponívelLimitada
Áreas de trabalho virtuaisSuportadoDependente de DE/WMDependente de DE/WMEspecífico do Spaces
Modo jogoSuportadoExperimentalLimitadoExperimental

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ódigoSignificado
401Token ausente ou inválido
415POST 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âmetroTipoPadrãoDescrição
monitorint1Índice do monitor
regionstring—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

actionParâmetros obrigatóriosParâmetros opcionaisDescrição
movex, yduration (padrão 0.15)Move o cursor para posição absoluta
clickx, ybutton (esquerdo/direito), clicks (padrão 1)Clica na posição
scrollclicksx, yRoda a roda do mouse (positivo=para cima)
dragx1, y1, x, yduration, buttonArrasta entre dois pontos
downbutton (padrão "esquerdo")—Pressiona e segura o botão do mouse
upbutton (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

actionParâmetros obrigatóriosDescrição
presskeyPressiona e solta uma tecla
downkeySegura uma tecla pressionada (rastreada pelo watchdog)
upkeySolta uma tecla pressionada
hotkeykeys (array)Combinação de teclas (ex.: ["ctrl","c"])
typetextDigita texto (Unicode, independente de layout)
Parâmetro opcionalPadrãoDescrição
expect_hwnd—Identificador da janela para verificar o foco (409 se houver incompatibilidade)
interval0.03Atraso 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âmetroTipoPadrãoDescrição
regionarray—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:

EndpointDescrição
GET /api/vision/frameQuadro JPEG individual (bruto ou base64)
GET /api/streamFluxo ao vivo MJPEG
POST /api/vision/diffDetecção de movimento baseada em texto (sem necessidade de visão)

GET /api/vision/frame

ParâmetroPadrãoDescrição
scale1.0Fator de redução (0.5 = metade do tamanho)
gray01 para escala de cinza
quality80Qualidade 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âmetroPadrãoDescrição
fps10Quadros por segundo (1-30)
quality70Qualidade JPEG
scale1.0Fator 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.

CorpoDescriçã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çãoObrigatórioOpcionalDescrição
focushwnd—Trazer janela para o primeiro plano
closehwndexpect_title, expect_processFechamento seguro via WM_CLOSE
killhwnd, pid—Forçar encerramento (estilo gerenciador de tarefas)
topmosthwnd—Definir sempre no topo
untopmosthwnd—Remover sempre no topo
maximizehwnd—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âmetroDescrição
hwnd (obrigatório)Identificador da janela
client1 = apenas área do cliente
ocr1 = 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çãoDescrição
typeDigitar texto (compatível com Unicode)
keyEnviar um pressionamento de tecla
hotkeyEnviar uma combinação de teclas
clickClicar nas coordenadas do cliente
scrollRolar a janela
dragArrastar dentro da janela

O parâmetro opcional mode controla o roteamento:

modoComportamento
auto (padrão)Decidido pela sonda de modo de entrada (veja abaixo)
backgroundForçar caminho PostMessage (janela mantém foco/ordem-z)
focusedForç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-mode relata uia para estes; /api/window/post então automaticamente usa o caminho SendInput com foco. /api/window/children permanece ú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çãoParâmetrosDescrição
switchnumberAlternar 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çãoParâmetrosDescrição
startsensitivity (padrão 12)Travar cursor no centro, habilitar entrada de jogo
movedx, dy, sensitivityRotacionar 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 .apikeys armazena 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:

  1. Baixa cloudflared.exe se ausente (portátil, sem necessidade de admin)
  2. Para instâncias remanescentes de uma execução anterior
  3. Inicia o servidor REST (porta 8745) e o servidor HTTP MCP (porta 8751)
  4. Aguarda até que ambos estejam saudáveis (sondas /token e /health)
  5. 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)

CategoriaFerramentas
Percepçãoget_info, ocr_screen, screenshot (bloco de imagem real para modelos de visão), motion_diff
Mouse / tecladomouse, keyboard (com expect_hwnd), get_held, release_all
Janelaslist_windows, focus_window, window_children, window_input_mode, window_post, window_capture_ocr, close_window
Modo jogogame (iniciar / mover / parar / heartbeat)

Qual transporte para quem

ConsumidorTransporteComando
Claude Desktop / Cursor / VS Code (local)stdiopython mcp_server.py
Claude Codestdioclaude mcp add ... (acima)
Agentes de nuvem / remotosstreamable-HTTPstart-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 por from mcp.server.fastmcp import FastMCP, Image e MCPServer por FastMCP.


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 Host não-loopback são recusadas com 421 — uma página de rebinding que resolve seu domínio para 127.0.0.1 não pode ler /token ou chamar a API
  • Respostas /token e / carregam Cache-Control: no-store para 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:

  1. Esc / Alt+Tab físico — entrada de hardware real; esta API não pode bloqueá-lo, e sempre funciona
  2. POST /api/release_all — liberação instantânea de tudo
  3. 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_hwnd em /api/key — se a janela em primeiro plano não corresponder, a digitação é recusada com 409
  • O caminho focado de /api/window/post verifica 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 com 409 — 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 region e scale são limitadas (400 caso contrário)
  • Payloads de text sã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 JogoAdequado?Notas
Minecraft (construção)✅ SimColocar blocos, andar, minerar
Minecraft (PvP)❌ NãoMuito lento para combate rápido
Jogos de turno✅ SimTempo amplo para ler→agir→verificar
RPG / aventura✅ SimInventário, diálogo, exploração
FPS rápido❌ NãoTempo de reação insuficiente
Jogos de quebra-cabeça✅ SimBaseado 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

AbordagemPayloadCaso de Uso
scale=1.0, gray=0~500 KBDetalhe completo
scale=0.5, gray=1~50 KBBom para a maioria dos modelos de visão
scale=0.25, gray=1~10 KBCompressão máxima
diff (texto)~1 KBAgentes somente texto
region=...VariávelFoco 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

  1. Faça um fork do repositório
  2. Crie um branch de funcionalidade
  3. Faça suas alterações
  4. Teste em uma máquina Windows
  5. 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.