gnome-screencast-mcp
# 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
Scored across 3 tools
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.
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.
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.
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.