Command Palette

Search for a command to run...

UnylyUnyly
Browse all

Fast Bridge Backend

FreeNot checked

Backend of fast_bridge using uiautomator2

GitHubEmbed

About

Backend of fast_bridge using uiautomator2

README

Backend da aplicação Fast Bridge — uma API REST, WebSocket e MCP para controle remoto de dispositivos Android via ADB e uiautomator2, com streaming de vídeo em tempo real via scrcpy.


Sumário


Visão Geral

O Fast Bridge Backend expõe dispositivos Android conectados via USB (ADB) como recursos HTTP, WebSocket e MCP. Com ele é possível:

  • Listar dispositivos conectados
  • Tirar screenshots
  • Consultar propriedades do sistema e informações de tela
  • Navegar no sistema de arquivos do dispositivo
  • Executar comandos shell via ADB
  • Enviar eventos de toque, teclas e texto
  • Fazer streaming de vídeo e controlar o dispositivo em tempo real via WebSocket (protocolo scrcpy)
  • Controlar dispositivos via LLMs usando o Model Context Protocol (MCP)

Arquitetura

fast_bridge_backend/
├── main.py                        # Ponto de entrada — FastAPI + Uvicorn
├── mcp_entry.py                   # Entrypoint MCP (FastMCP) em STDIO
├── pyproject.toml                 # Dependências gerenciadas via UV
├── app/
│   ├── dependencies.py            # DeviceManager singleton + get_device_manager()
│   ├── binaries/                  # scrcpy-server-v*.jar
│   ├── controller/
│   │   ├── scrcpy.py              # ScrcpyServer: streaming de vídeo + controle WebSocket
│   │   ├── touch_controller.py    # Protocolo binário de toque para scrcpy
│   │   ├── android_input.py       # Constantes de input Android (KeyeventAction, MetaState)
│   │   ├── keycode.py             # Enum de KeyCodes Android
│   │   └── file_manager.py        # list_files_by_path() — wrapper de ls -la
│   ├── core/
│   │   ├── constants.py           # Constantes globais (PORT)
│   │   └── logger_config.py       # Configuração do Loguru
│   ├── model/
│   │   ├── adboutput.py           # Modelo Pydantic AdbResponse
│   │   └── file_entry.py          # Modelos FileEntry e FileManagerResponse + parse_ls_output()
│   ├── routes/
│   │   ├── device.py              # Endpoints REST e WebSocket (usa DeviceService via Depends)
│   │   └── health.py              # GET /health
│   └── services/
│       └── device_service.py      # DeviceService: toda a lógica de negócio + get_device_service()
├── logs/                          # Logs rotativos gerados pelo Loguru
└── tests/
    └── test_device_api.py         # Testes unitários com mocks

Injeção de Dependência

As rotas não gerenciam conexões diretamente. O padrão é:

DeviceManager (singleton em app/dependencies.py)
    └── DeviceService (instanciado por request via Depends)
            └── Rotas (recebem DeviceService via Depends(get_device_service))
# Padrão nas rotas
@router.get("/device/{serial}/screenshot")
def screenshot(serial: str, svc: DeviceService = Depends(get_device_service)):
    return svc.screenshot(serial)

Os testes sobrescrevem a dependência via app.dependency_overrides:

app.dependency_overrides[get_device_service] = lambda: DeviceService(_make_mock_manager(mock_device))

Fluxo de vídeo via WebSocket

Cliente (navegador)
    │
    ▼  ws://localhost:8000/ws/device/{serial}/control
FastAPI WebSocket
    │
    ├── ScrcpyServer._stream_video_to_websocket()  ─►  frames JPEG para o cliente
    └── ScrcpyServer._handle_control_websocket()   ◄─  eventos JSON do cliente
            │
            └── ScrcpyTouchController  ─►  protocolo binário scrcpy ─►  dispositivo

Requisitos

  • Python 3.12+
  • ADB instalado e acessível no PATH
  • Dispositivo Android conectado via USB com depuração USB habilitada
  • Arquivo JAR do scrcpy server em app/binaries/scrcpy-server-v2.7.jar

Principais dependências Python:

Pacote Versão
fastapi 0.135.1
uvicorn 0.42.0
uiautomator2 3.5.0
adbutils 2.12.0
pydantic 2.12.5
pillow 12.1.1
loguru 0.7.3
mcp ≥1.27.1
av 17.0.0

Instalação

O projeto usa UV para gerenciamento de dependências.

# Clone o repositório
git clone <url-do-repositorio>
cd fast_bridge_backend

# Instale as dependências com UV (recomendado)
uv sync

# Ou com pip tradicional
python -m venv .venv
source .venv/bin/activate   # Linux / macOS
# .venv\Scripts\activate    # Windows
pip install -r requirements.txt

Executando o Projeto

python main.py

O servidor sobe em http://localhost:8000.

  • Swagger UI: http://localhost:8000/docs
  • MCP (STDIO): python mcp_entry.py

Endpoints da API

Health

GET /health

Verifica se o servidor está em execução.

Resposta 200

{ "status": "ok" }

Dispositivos

GET /devices

Lista todos os dispositivos Android conectados via ADB.

Resposta 200

[
  {
    "serialno": "RQCTA0823SP",
    "devpath": "usb:1-2",
    "state": "device"
  }
]

Captura e Informações

GET /device/{device_serial}/screenshot

Retorna um screenshot do dispositivo como imagem JPEG.

Parâmetro Tipo Descrição
device_serial path Serial do dispositivo
display_id query ID do display (padrão: 0)

Resposta 200image/jpeg


GET /device/{device_serial}/screen_info

Retorna as dimensões da tela do dispositivo.

Resposta 200

{
  "width": 1080,
  "height": 2400
}

GET /device/{device_serial}/prop/{shell_property}

Consulta uma propriedade do sistema Android via getprop.

Exemplo: GET /device/emulator-5554/prop/ro.product.model

Resposta 200AdbResponse

{
  "device_serial": "emulator-5554",
  "stdout": "Pixel 6",
  "exit_code": 0
}

Resposta 505 — Erro de ADB.


GET /device/{device_serial}/window_dump

Retorna o dump da hierarquia de UI do dispositivo em XML (uiautomator2).

Parâmetro Tipo Valores Descrição
format query xml Formato de saída (padrão: xml). Outros valores retornam 400.

Resposta 200text/xml


Gerenciador de Arquivos

GET /device/{device_serial}/file_manager

Lista arquivos e diretórios em um caminho do dispositivo via ls -la.

Parâmetro Tipo Descrição
device_serial path Serial do dispositivo
path query Caminho no dispositivo (padrão: .)

Resposta 200FileManagerResponse

{
  "path": "/sdcard",
  "entries": [
    {
      "name": "Download",
      "permissions": "drwxrwx--x",
      "is_dir": true,
      "is_symlink": false,
      "owner": "root",
      "group": "sdcard_rw",
      "size": 4096,
      "modified_at": "2024-01-15 10:30",
      "symlink_target": null
    }
  ]
}

Entrada (Input)

POST /device/{device_serial}/input/keyevent

Envia um evento de tecla Android.

Parâmetro Tipo Descrição
keycode query Código da tecla Android (ex.: 4 = BACK, 66 = ENTER)
repeat query Número de repetições com longpress (padrão: 0)
metastate query Flags de meta state (padrão: 0)

Resposta 200

{ "detail": "Key event 4 sent to device emulator-5554" }

PUT /device/{device_serial}/input/text

Envia uma string de texto para o dispositivo.

Parâmetro Tipo Descrição
text query Texto a ser digitado

Resposta 200

{ "detail": "Text sent to device emulator-5554" }

PUT /device/{device_serial}/input/touch

Envia um evento de toque (tap) em coordenadas absolutas.

Parâmetro Tipo Descrição
x query Coordenada X em pixels
y query Coordenada Y em pixels

Resposta 200

{ "detail": "Touch event sent to device emulator-5554 at (540, 960)" }

Shell ADB

POST /device/{device_serial}

Executa um comando shell no dispositivo via ADB. O body deve ser uma lista de strings (tokens).

Body application/json

["pm", "list", "packages"]

Resposta 200AdbResponse

{
  "device_serial": "emulator-5554",
  "stdout": "package:com.example.app\n...",
  "exit_code": 0
}

Resposta 505 — Erro de ADB.


WebSocket — Controle em Tempo Real

WS /ws/device/{device_serial}/control

Canal bidirecional que combina streaming de vídeo (scrcpy) com controle do dispositivo.

Mensagens do cliente → servidor (JSON):

type Campos adicionais Descrição
touchDown xP, yP (0.0–1.0) Toque iniciado (coordenadas percentuais)
touchMove xP, yP (0.0–1.0) Arrastar
touchUp xP, yP (0.0–1.0) Toque liberado
keyEvent data.eventNumber Evento de tecla Android
text detail Envio de texto via broadcast (am broadcast)
ping Keepalive

Coordenadas xP/yP são percentuais (0.0–1.0). O servidor converte para pixels absolutos usando a resolução obtida no handshake do scrcpy.

Mensagens do servidor → cliente:

  • Frames de vídeo binários JPEG
  • {"type": "pong"} em resposta ao ping

Servidor MCP

O servidor MCP usa somente transporte STDIO.

Execução direta:

python mcp_entry.py

Configuração no Claude Desktop:

{
  "mcpServers": {
    "fast-bridge": {
      "command": "python",
      "args": ["mcp_entry.py"]
    }
  }
}

Skill fast-bridge para Agentes

Além da integração MCP padrão, o projeto inclui um fluxo operacional para agentes (ex.: Copilot CLI) controlarem Android de forma confiável.

Configuração recomendada do servidor MCP para a skill:

  • Command: .venv/bin/python (ou python com o ambiente virtual ativo)
  • Argument: mcp_entry.py (a partir da raiz do projeto)

Regras operacionais (loop obrigatório)

  1. Descoberta: chame list_connected_devices.
  2. Inspeção: chame get_ui_hierarchy(serial) e localize o alvo no XML.
  3. Ação: execute execute_adb_command(serial, command).
  4. Verificação: sempre chame get_ui_hierarchy (ou take_screenshot) após a ação.

Esse ciclo evita automações “cegas” e garante confirmação de estado em cada etapa.

Coordenadas de toque (tap)

Para tocar em um elemento da UI, use bounds do XML:

bounds="[left,top][right,bottom]"
center_x = (left + right) / 2
center_y = (top + bottom) / 2

Depois envie:

["input", "tap", "<center_x>", "<center_y>"]

Ferramentas usadas no fluxo

As ferramentas MCP expostas pelo backend são:

  • list_connected_devices
  • get_ui_hierarchy(serial)
  • take_screenshot(serial)
  • execute_adb_command(serial, command)

Em alguns clientes/agentes, elas podem aparecer com prefixo de namespace (por exemplo, fb-list_connected_devices, fb-get_ui_hierarchy, etc.). A funcionalidade é a mesma.

Segurança do execute_adb_command

  • Whitelist de comandos: input, am, pm, dumpsys, getprop, settings, service, wm, cmd.
  • Bloqueio de metacaracteres de shell (;, |, &, $, `, >, <, quebras de linha).
  • O comando deve ser enviado tokenizado (list[str]), sem concatenação shell.

Troubleshooting rápido

  • Skill carregou, mas as tools não aparecem no agente:
    • valide se o MCP está rodando com o command/args acima;
    • confira se o cliente realmente conectou ao servidor fast-bridge nesta sessão.
  • am start retorna sucesso, mas a UI não mudou:
    • execute o passo de Verificação e tente uma intent mais explícita (-n package/.Activity ou deep link).
  • XML grande/difícil de interpretar:
    • complemente com take_screenshot(serial) para confirmação visual.

Ferramentas disponíveis

list_connected_devices

Retorna os seriais de todos os dispositivos ADB conectados.

Retorno: list[str]


get_ui_hierarchy(serial)

Retorna o dump XML da hierarquia de UI da tela atual do dispositivo.

Parâmetro Tipo Descrição
serial str Serial ADB do dispositivo

Retorno: str — XML UTF-8 da hierarquia de views.


take_screenshot(serial)

Captura um screenshot e retorna como string base64 (JPEG).

Parâmetro Tipo Descrição
serial str Serial ADB do dispositivo

Retorno: str — JPEG codificado em base64.


execute_adb_command(serial, command)

Executa um comando ADB shell autorizado no dispositivo.

Parâmetro Tipo Descrição
serial str Serial ADB do dispositivo
command list[str] Comando tokenizado, ex.: ["input", "tap", "540", "960"]

Comandos permitidos: input, am, pm, dumpsys, getprop, settings, service, wm, cmd.

Metacaracteres de shell (;, |, &, $, `, >, <) são rejeitados.

Retorno:

{ "stdout": "...", "exit_code": 0 }

Modelos de Dados

AdbResponse

class AdbResponse(BaseModel):
    device_serial: str   # Serial do dispositivo
    stdout: str          # Saída do comando
    exit_code: int       # 0 = sucesso, 1 = erro

FileEntry

class FileEntry(BaseModel):
    name: str
    permissions: str         # ex.: "drwxr-xr-x"
    is_dir: bool
    is_symlink: bool
    owner: str
    group: str
    size: int
    modified_at: str         # ex.: "2024-01-15 10:30"
    symlink_target: str | None

FileManagerResponse

class FileManagerResponse(BaseModel):
    path: str
    entries: list[FileEntry]

Estrutura do Projeto

Módulo Responsabilidade
main.py Configuração do app FastAPI, CORS e Uvicorn
mcp_entry.py Entrypoint do servidor MCP em transporte STDIO
app/dependencies.py DeviceManager singleton; provedor get_device_manager()
app/services/device_service.py DeviceService: toda a lógica de negócio; provedor get_device_service()
app/routes/device.py Endpoints REST e WebSocket; injetam DeviceService via Depends
app/routes/health.py GET /health
app/controller/scrcpy.py Gerencia o servidor scrcpy no dispositivo, streaming de vídeo e controle via WebSocket
app/controller/touch_controller.py Serializa eventos de toque no protocolo binário do scrcpy
app/controller/android_input.py Enums KeyeventAction e MetaState
app/controller/keycode.py Enum KeyCode com todos os keycodes Android
app/controller/file_manager.py list_files_by_path() — executa ls -la e retorna FileManagerResponse
app/model/adboutput.py Schema Pydantic AdbResponse
app/model/file_entry.py Schemas FileEntry e FileManagerResponse + parse_ls_output()
app/core/constants.py Constantes globais
app/core/logger_config.py Loguru configurado com rotação e compressão

Testes

Os testes usam pytest com unittest.mock para isolar dependências ADB. As dependências são injetadas via app.dependency_overrides.

pytest tests/
# Ou um teste específico:
pytest tests/test_device_api.py::test_send_adb_shell_success

Casos de teste cobertos em tests/test_device_api.py:

  • GET /devices — listagem de dispositivos mockados
  • POST /device/{serial} — execução de shell com sucesso e com erro (505)
  • GET /device/{serial}/prop/{property} — consulta de propriedade

Logging

Configurado via Loguru. Logs são emitidos para:

  • stderr — nível INFO
  • logs/app_YYYY-MM-DD.log — nível INFO, rotação a cada 5 MB, retenção de 7 dias, compressão ZIP

Atenção: Use from app.core import log em todo código dentro de app/. Nunca use print() ou logging.getLogger().


Frontend

O frontend da aplicação está disponível em fast-bridge-nine.vercel.app e se comunica com este backend via HTTP e WebSocket.

from github.com/desodre/fast_bridge_backend

Installing Fast Bridge Backend

This server has no published package — it is built from source. Open the repository and follow its README.

▸ github.com/desodre/fast_bridge_backend

FAQ

Is Fast Bridge Backend MCP free?

Yes, Fast Bridge Backend MCP is free — one-click install via Unyly at no cost.

Does Fast Bridge Backend need an API key?

No, Fast Bridge Backend runs without API keys or environment variables.

Is Fast Bridge Backend hosted or self-hosted?

Self-hosted: the server runs locally on your machine via the install command above.

How do I install Fast Bridge Backend in Claude Desktop, Claude Code or Cursor?

Open Fast Bridge Backend on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.

Related MCPs

Compare Fast Bridge Backend with

Not sure what to pick?

Find your stack in 60 seconds

Author?

Embed badge for your README

Browse similar

All ai MCPs