Skip to main content
Glama
quinho981

gnome-screencast-mcp

by quinho981
README.md
# gnome-screencast-mcp

Gravação de tela no GNOME, controlada por linha de comando ou por um agente de IA
via [MCP](https://modelcontextprotocol.io).

Usa o gravador **nativo do GNOME Shell** (a interface D-Bus `org.gnome.Shell.Screencast`,
a mesma por trás do atalho <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>Shift</kbd>+<kbd>R</kbd>).
Não depende de ffmpeg, wf-recorder nem de qualquer binário externo de captura.

## Para que serve

Automatizar gravações de tela sem tocar na interface gráfica: demonstrações, evidências de
bug, documentação de fluxos, registro de sessões de teste. Como cada comando devolve JSON,
serve tanto para scripts quanto para um agente que precisa gravar o que está fazendo.

O problema que ele resolve: chamar o D-Bus do GNOME diretamente **não funciona** para
gravar. O Shell encerra a gravação assim que o cliente D-Bus que a iniciou sai do
barramento, então um `gdbus call` avulso produz um arquivo com um único quadro e duração
`0:00`. A solução aqui é um processo auxiliar que mantém a conexão aberta durante toda a
gravação e a encerra de forma limpa no stop — só assim o WebM sai com duração e índice
corretos.

## Instalação

Nada para clonar nem compilar. Quatro passos, do zero ao primeiro vídeo gravado.

> **Ainda não está no PyPI.** Por enquanto o `uv` instala direto deste repositório no
> GitHub — funciona igual, só o comando é um pouco mais longo. Assim que publicarmos no
> PyPI, `gnome-screencast-mcp` sozinho basta; os dois viram intercambiáveis.

### Passo 1 — confira os requisitos

| Requisito | Por quê | Verificar |
|---|---|---|
| GNOME Shell, sessão gráfica ativa (Wayland ou X11) | É o próprio GNOME que grava; testado no GNOME Shell 42 | `gnome-shell --version` |
| PyGObject (`python3-gi`) | Mantém viva a conexão D-Bus durante a gravação; não instala via pip | `python3 -c "import gi"` — se der erro: `sudo apt install python3-gi` |
| [`uv`](https://docs.astral.sh/uv/) | Instala e roda o pacote, sem venv manual | `uv --version` — se faltar: `curl -LsSf https://astral.sh/uv/install.sh \| sh` |

Se as três passarem sem erro, siga para o passo 2.

### Passo 2 — instale

```bash
uv tool install --from git+https://github.com/quinho981/gnome-screencast-mcp gnome-screencast-mcp
```

Isso põe três executáveis no seu `PATH`:

| Executável | Papel |
|---|---|
| `gnome-screencast-start` | Inicia a gravação pela linha de comando. |
| `gnome-screencast-stop` | Encerra a gravação pela linha de comando. |
| `gnome-screencast-mcp` | Servidor MCP (transporte stdio) — é o que um agente de IA chama. |

Se o terminal avisar que o diretório de instalação não está no `PATH`, rode o comando que
ele mesmo sugere (geralmente `uv tool update-shell`) e abra um terminal novo.

### Passo 3 — teste

```bash
gnome-screencast-start && sleep 3 && gnome-screencast-stop
```

Deve sair um JSON com `"status": "recording"`, uma pausa de 3 segundos, e outro JSON com
`"status": "stopped"` e `duration_seconds` perto de 3. Se saiu isso, está tudo funcionando
— o arquivo `.webm` ficou no seu diretório de vídeos.

Deu errado? Vá direto para [Problemas comuns](#problemas-comuns).

### Passo 4 — escolha como usar

- **Pela linha de comando:** já está pronto — veja [Uso pela linha de
  comando](#uso-pela-linha-de-comando) para as opções de `-o`, `-f`, `-a`.
- **Por um agente de IA (Claude Code, Cursor, opencode, etc.):** falta registrar o servidor
  MCP no seu cliente — veja [Uso via MCP](#uso-via-mcp), que tem o passo a passo de cada um.

### Sem passos: só quer o MCP funcionando num agente

Se o único uso é MCP, não precisa instalar nada à mão — o próprio cliente baixa o pacote na
hora de rodar. Confira os requisitos do passo 1, pule os passos 2 e 3, e vá direto para
[Uso via MCP](#uso-via-mcp).

### Desenvolvendo o próprio projeto

Só para quem vai alterar este repositório:

```bash
git clone https://github.com/quinho981/gnome-screencast-mcp
cd gnome-screencast-mcp
uv run gnome-screencast-mcp        # servidor MCP a partir do código local
bash bin/start-recording.sh        # scripts de gravação, sem instalar nada
bash bin/stop-recording.sh
```

## Estrutura

| Arquivo | Papel |
|---|---|
| `bin/start-recording.sh` | Inicia a gravação. Imprime JSON e retorna na hora. |
| `bin/stop-recording.sh` | Encerra e espera o arquivo ser finalizado. |
| `bin/recorder-daemon.py` | Processo auxiliar que segura a conexão D-Bus. Não chame direto. |
| `gnome_screencast_mcp/server.py` | Servidor MCP; converte chamadas de ferramenta em execuções dos scripts. |
| `gnome_screencast_mcp/cli.py` | Executáveis `gnome-screencast-start` e `-stop` de uma instalação. |
| `.mcp.json` | Registro do servidor MCP para quem abrir este projeto no Claude Code. |

Os scripts ficam em `bin/` para continuarem utilizáveis direto de um clone; o build do
wheel os copia para dentro do pacote, e o servidor os encontra nos dois lugares.

## Uso pela linha de comando

```bash
# Tela inteira, 30 fps, arquivo com data e hora em ~/Vídeos
gnome-screencast-start

# ... faça o que precisa ser gravado ...

gnome-screencast-stop
```

Num clone, os equivalentes são `bash bin/start-recording.sh` e `bash bin/stop-recording.sh`.

O start devolve o caminho do arquivo assim que a gravação começa:

```json
{
  "status": "recording",
  "file": "/home/user/Vídeos/screencast-20260824-152940.webm",
  "mode": "screen",
  "framerate": 30,
  "draw_cursor": true,
  "started_at": "2026-08-24T15:29:40-0300",
  "pid": 183615
}
```

E o stop, o resumo do que foi gravado:

```json
{
  "status": "stopped",
  "file": "/home/user/Vídeos/screencast-20260824-152940.webm",
  "size_bytes": 361637,
  "duration_seconds": 4.488
}
```

### Opções do start

| Opção | Efeito |
|---|---|
| `-o, --output ARQUIVO` | Caminho do `.webm` de saída. Não pode conter `%`. |
| `-f, --framerate N` | Quadros por segundo (padrão: 30). |
| `-a, --area X Y L A` | Grava só a região retangular informada, em pixels. |
| `-c, --no-cursor` | Não desenha o ponteiro do mouse. |

Exemplo — canto superior esquerdo, 1280×720, 60 fps, sem cursor:

```bash
gnome-screencast-start -a 0 0 1280 720 -f 60 --no-cursor -o /tmp/demo.webm
```

### Opções do stop

| Opção | Efeito |
|---|---|
| `-t, --timeout N` | Segundos de espera até o arquivo ser finalizado (padrão: 20). |
| `-q, --quiet` | Não imprime o JSON de resultado. |

### Códigos de saída

Ambos os comandos usam `0` para sucesso e `1` para erro de uso ou de ambiente. Além disso:

- `start`: `2` já existe uma gravação em andamento · `3` o GNOME Shell recusou iniciar
- `stop`: `2` nenhuma gravação em andamento · `3` o arquivo não foi finalizado a tempo

## Uso via MCP

Registrar o servidor faz a gravação virar uma capacidade do agente: ele chama
`start_recording` e `stop_recording` como ferramentas tipadas, sem precisar de acesso ao
shell.

### Ferramentas expostas

| Ferramenta | O que faz |
|---|---|
| `start_recording(output?, framerate=30, draw_cursor=true, area?)` | Inicia e retorna na hora. `area` é `[x, y, largura, altura]`. |
| `stop_recording(timeout=20)` | Encerra e devolve caminho, tamanho e duração. |
| `recording_status()` | `idle`, `recording` (com `elapsed_seconds`) ou `stale`. |

`recording_status` é a forma barata de checar antes de agir — evita tentar iniciar uma
gravação que já existe, ou parar uma que não existe.

### O comando, em qualquer cliente

O servidor é um processo **stdio** comum, e o comando é o mesmo em toda parte:

```
comando:    uvx
argumentos: gnome-screencast-mcp
```

Se você fez `uv tool install`, o comando é só `gnome-screencast-mcp`, sem argumentos.

> **Enquanto o pacote não estiver no PyPI**, use esta lista de argumentos em vez de
> `["gnome-screencast-mcp"]` em todos os exemplos abaixo:
>
> ```json
> ["--from", "git+https://github.com/quinho981/gnome-screencast-mcp", "gnome-screencast-mcp"]
> ```
>
> Assim que o pacote for publicado, volte para a forma curta — os exemplos já estão
> escritos nela.

Duas coisas quebram configurações que parecem certas:

1. **`uvx` pode não estar no `PATH` do cliente.** Clientes iniciados por um lançador
   gráfico (Cursor, VS Code, Zed, Claude Desktop) costumam herdar um `PATH` mínimo, sem
   `~/.local/bin`. Se o servidor falhar com `uvx: command not found`, troque `uvx` pela
   saída de `command -v uvx` — normalmente `/home/<seu-usuário-do-sistema>/.local/bin/uvx`.
2. **A gravação precisa do barramento da sessão.** O D-Bus do GNOME é alcançado através de
   `DBUS_SESSION_BUS_ADDRESS` e `XDG_RUNTIME_DIR`. Um cliente lançado dentro da sua sessão
   gráfica já as herda. Um cliente em contêiner, snap, flatpak ou sessão SSH, não — nesse
   caso declare as duas no bloco `env` do servidor:

   ```json
   "env": {
     "DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1000/bus",
     "XDG_RUNTIME_DIR": "/run/user/1000"
   }
   ```

   Os valores corretos para a sua máquina saem de
   `echo $DBUS_SESSION_BUS_ADDRESS $XDG_RUNTIME_DIR` num terminal da sessão gráfica.

### Claude Code

```bash
claude mcp add screen-recorder --scope user \
  -- uvx --from git+https://github.com/quinho981/gnome-screencast-mcp gnome-screencast-mcp
```

Depois de publicado no PyPI, simplifica para `-- uvx gnome-screencast-mcp`.

Reinicie a sessão e confirme com `/mcp` que `screen-recorder` aparece conectado.

Dentro deste repositório não é preciso nem isso: o `.mcp.json` versionado aqui já registra
o servidor a partir do código local — basta aprová-lo ao abrir o diretório.

### Codex CLI

```bash
codex mcp add screen-recorder \
  -- uvx --from git+https://github.com/quinho981/gnome-screencast-mcp gnome-screencast-mcp
```

Depois de publicado no PyPI, simplifica para `-- uvx gnome-screencast-mcp`.

Ou à mão, em `~/.codex/config.toml`:

```toml
[mcp_servers.screen-recorder]
command = "uvx"
args = ["gnome-screencast-mcp"]
```

### opencode

Em `opencode.json` na raiz do seu projeto, ou em `~/.config/opencode/opencode.json` para
valer em todos:

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "screen-recorder": {
      "type": "local",
      "enabled": true,
      "command": ["gnome-screencast-mcp"]
    }
  }
}
```

Isso pressupõe o `uv tool install` do [passo 2 da instalação](#passo-2--instale) — o
comando é só o nome do executável, já resolvido pelo `PATH`. O opencode marca o servidor
como falho (e reverte `enabled` para `false` sozinho) se o comando não sobe de primeira, e
`uvx gnome-screencast-mcp` cai nesse caso enquanto o pacote não estiver no PyPI: cada
chamada tentaria resolvê-lo ali e falharia. Se preferir não instalar de vez, a forma que
funciona sem instalar é a mesma nota de [git+](#o-comando-em-qualquer-cliente) dos outros
clientes:

```json
"command": ["uvx", "--from", "git+https://github.com/quinho981/gnome-screencast-mcp", "gnome-screencast-mcp"]
```

O opencode é o único caso em que o comando é uma lista única, e não um `command` separado
dos `args`.

### Cursor

Em `~/.cursor/mcp.json` (global) ou `.cursor/mcp.json` (só naquele projeto):

```json
{
  "mcpServers": {
    "screen-recorder": {
      "command": "uvx",
      "args": ["gnome-screencast-mcp"]
    }
  }
}
```

O Cursor é lançado pelo ambiente gráfico: se ele não conectar, o motivo mais provável é o
`PATH` sem `uvx`. Veja o item 1 de [O comando, em qualquer
cliente](#o-comando-em-qualquer-cliente).

### Gemini CLI

```bash
gemini mcp add screen-recorder \
  uvx --from git+https://github.com/quinho981/gnome-screencast-mcp gnome-screencast-mcp
```

Depois de publicado no PyPI, simplifica para `uvx gnome-screencast-mcp`.

Ou à mão, em `~/.gemini/settings.json` (global) ou `.gemini/settings.json` (por projeto),
no mesmo formato `mcpServers` mostrado para o Cursor.

### VS Code (GitHub Copilot)

Em `.vscode/mcp.json` no projeto. Repare que a chave é `servers`, não `mcpServers`, e que
o tipo é explícito:

```json
{
  "servers": {
    "screen-recorder": {
      "type": "stdio",
      "command": "uvx",
      "args": ["gnome-screencast-mcp"]
    }
  }
}
```

### Windsurf

Em `~/.codeium/windsurf/mcp_config.json`, no mesmo formato `mcpServers` do Cursor.

### Zed

No `settings.json` do Zed, sob `context_servers`:

```json
{
  "context_servers": {
    "screen-recorder": {
      "source": "custom",
      "command": "uvx",
      "args": ["gnome-screencast-mcp"],
      "env": {}
    }
  }
}
```

### Claude Desktop

No Linux, em `~/.config/Claude/claude_desktop_config.json`, no mesmo formato `mcpServers`
do Cursor. O app precisa ser reiniciado por completo depois da edição.

### Outros clientes

Se o seu cliente não estiver listado, procure na documentação dele onde ficam os
"MCP servers" e informe o comando `uvx` com o argumento `gnome-screencast-mcp`. Na prática
só há duas variações de formato em todo o ecossistema: o par `command` + `args` separados
(a maioria) e o `command` como lista única (opencode).

### Sem `uv`

O pacote é um projeto Python comum e o `pip` dá conta:

```bash
pip install --user git+https://github.com/quinho981/gnome-screencast-mcp
```

(Depois de publicado no PyPI: `pip install --user gnome-screencast-mcp`.)

O comando do cliente passa a ser `gnome-screencast-mcp`, sem argumentos. Um ambiente
virtual dedicado também funciona — nesse caso aponte para o executável de dentro dele; o
servidor remove o próprio venv do `PATH` que repassa aos scripts, então eles continuam
achando o PyGObject do sistema.

### Testar o servidor sem um cliente

Antes de brigar com a configuração de um agente, vale confirmar que o servidor sobe:

```bash
printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
  | uvx --from git+https://github.com/quinho981/gnome-screencast-mcp gnome-screencast-mcp
```

(Depois de publicado no PyPI, `uvx gnome-screencast-mcp` sozinho já basta.)

Deve sair a resposta do `initialize` seguida das três ferramentas. Na primeira execução o
`uvx` também imprime uma linha de instalação em stderr.

## Como funciona

```
start-recording.sh
  └─ setsid recorder-daemon.py        (sobrevive ao script que o criou)
       ├─ D-Bus: Screencast(...)      → o GNOME Shell começa a gravar
       ├─ escreve o estado em $XDG_RUNTIME_DIR/screen-recorder/current.json
       └─ fica vivo, segurando a conexão, até receber SIGTERM

stop-recording.sh
  └─ SIGTERM no pid do estado
       └─ daemon: D-Bus StopScreencast pela mesma conexão
            └─ espera o GStreamer fechar o WebM e escreve o resumo final
```

O arquivo de estado garante que só exista uma gravação por vez — limite do próprio GNOME
Shell, que suporta uma sessão de screencast simultânea.

Se o daemon morrer sem limpar (por exemplo, num logout), o arquivo de estado fica órfão:
`recording_status` reporta `stale` e o próximo `start_recording` o remove sozinho.

## Detalhes de implementação

Três armadilhas que o pacote precisa contornar:

- **Captura de saída.** O servidor executa os scripts com a saída redirecionada para
  arquivos temporários, não para pipes. O daemon herda os descritores de saída, então um
  pipe só veria EOF no fim da gravação — e a chamada de `start_recording` travaria até lá.
- **Ambiente Python.** Instalado, o servidor roda dentro de um ambiente virtual, que fica
  no início do `PATH`. Os scripts passariam a resolver `python3` para ele, onde o PyGObject
  do sistema não existe. O servidor remove o venv do ambiente que os scripts herdam.
- **Bit de execução.** Os scripts são invocados como `bash script.sh` e o daemon como
  `python3 daemon.py`, nunca diretamente: a permissão de execução não sobrevive de forma
  confiável ao empacotamento em wheel.

## Problemas comuns

**`PyGObject não encontrado`** — instale com `sudo apt install python3-gi`. Se aparecer só
ao usar o MCP e não na linha de comando, o venv está vazando para o `PATH` dos scripts.

**O agente não lista as ferramentas** — o servidor nem chegou a subir. Rode o teste de
[Testar o servidor sem um cliente](#testar-o-servidor-sem-um-cliente); se ele passar, o
problema está na configuração do cliente, quase sempre no `uvx` fora do `PATH` (item 1 de
[O comando, em qualquer cliente](#o-comando-em-qualquer-cliente)).

**`o GNOME Shell recusou iniciar a gravação`** — normalmente não há sessão GNOME acessível.
O servidor MCP herda o ambiente de quem o iniciou, e o D-Bus precisa de
`DBUS_SESSION_BUS_ADDRESS` e `XDG_RUNTIME_DIR`. Se o cliente MCP rodar num ambiente
confinado (contêiner, snap, flatpak, serviço, SSH), declare as duas variáveis no bloco
`env` do servidor — veja o item 2 de [O comando, em qualquer
cliente](#o-comando-em-qualquer-cliente).

**`gravação já em andamento`** — chame `stop_recording`, ou `gnome-screencast-stop`. Para
inspecionar o estado à mão: `cat $XDG_RUNTIME_DIR/screen-recorder/current.json`.

**Vídeo com duração `0:00`** — sinal de que a gravação foi iniciada por fora destes
comandos, com um cliente D-Bus que não sobreviveu. Use o `gnome-screencast-start`.

Log do processo auxiliar: `$XDG_RUNTIME_DIR/screen-recorder/daemon.log`.

## Licença

MIT — veja [LICENSE](LICENSE).

## Publicar uma versão

```bash
uv build          # gera dist/*.whl e dist/*.tar.gz
uv publish        # envia ao PyPI
```

A versão fica em `pyproject.toml`.

TDQS

A4.4/5.0

Scored across 3 tools

Disambiguation5/5

Start, stop, and status are three clearly distinct lifecycle operations with no overlap. An agent can easily tell them apart based on name and description.

Naming Consistency4/5

start_recording and stop_recording follow the verb_noun pattern consistently, while recording_status is slightly inconsistent as a noun phrase rather than get_recording_status. Overall the naming is still predictable and readable.

Tool Count5/5

Three tools is the right size for the server's single-purpose scope: start, stop, and query status. Each tool fills an essential role without redundancy.

Completeness5/5

The screen-recording lifecycle is fully covered: start, stop, and monitor status. The stale-state handling also addresses the natural failure case, making the surface complete for the stated domain.

Maintenance

ActivityMaintained
ResponsivenessNo issues