Skip to main content
Glama
README.md
# ol-ops-mcp

Servidor MCP (Model Context Protocol) para operação de hosts **Oracle Linux 8+** via
SSH, com capabilities curadas em YAML — **sem `exec(command: string)` livre**. O modelo
chama capabilities nomeadas (`system.disk_usage`, não `df -h`); parâmetros são tipados e
validados; toda mutação passa por confirmação em duas etapas; e tudo é auditado.

Ver [`ARCHITECTURE.md`](ARCHITECTURE.md) para o design completo, [`ROADMAP.md`](ROADMAP.md)
para o estado da implementação e [`SECURITY.md`](SECURITY.md) para modelo de ameaça e
hardening de deploy — **leia o `SECURITY.md` antes de apontar este servidor para um host
de produção**.

## Por que não é só mais um MCP de SSH

MCPs de SSH genéricos expõem um `exec` livre: qualquer texto que chegue ao contexto do
modelo (uma página web, um log, a saída de outro MCP) se torna instrução em potencial.
Este servidor inverte o default — comandos vivem num catálogo YAML versionado, com
parâmetros tipados (nunca `string` livre), renderização via `shlex.quote`, e saída remota
sempre envelopada como dado não confiável, nunca como instrução. Se um diagnóstico
realmente não cabe em capability curada, existe uma única porta de escape —
`ol_raw_exec` — gated por três barreiras independentes (ver `SECURITY.md`).

## Instalação

### De um clone (desenvolvimento)

```bash
uv venv --python 3.11 --seed
source .venv/bin/activate      # ou .venv\Scripts\activate no Windows
uv pip install -e ".[dev]"

# validação rápida
ruff check src tests && ruff format --check src tests
mypy --strict src
pytest -m "not integration"

# validação completa (exige podman ou docker; roda containers OL8/OL9 reais)
pytest
```

### Via uvx

```bash
uvx ol-ops-mcp
```

## Configuração mínima

1. Gere um par de chaves dedicado para o servidor (não reutilize uma chave pessoal):

   ```bash
   ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_claudeops -C claudeops@ol-ops-mcp
   ```

2. Registre a chave pública no `authorized_keys` do usuário remoto — com as restrições
   descritas em `SECURITY.md` (`restrict,pty=no,...`).

3. Registre a host key no seu `known_hosts` local. O servidor **não** aceita host
   desconhecido automaticamente (sem `AutoAddPolicy`) — é preciso este passo manual:

   ```bash
   ssh-keyscan -t ed25519 -H <address> >> ~/.ssh/known_hosts
   ```

4. Copie [`examples/hosts.toml.example`](examples/hosts.toml.example) para
   `~/.config/ol-ops-mcp/hosts.toml` (ou o caminho apontado por `OL_OPS_MCP_CONFIG`) e
   ajuste os hosts:

   ```bash
   mkdir -p ~/.config/ol-ops-mcp
   cp examples/hosts.toml.example ~/.config/ol-ops-mcp/hosts.toml
   chmod 600 ~/.config/ol-ops-mcp/hosts.toml
   ```

   O servidor recusa subir se o arquivo estiver mais aberto que `0600` (em sistemas
   POSIX). Um esqueleto mínimo:

   ```toml
   [server]
   audit_path = "~/.local/state/ol-ops-mcp/audit.jsonl"
   known_hosts = "~/.ssh/known_hosts"

   [defaults]
   mode     = "readonly"
   user     = "claudeops"
   key_file = "~/.ssh/id_ed25519_claudeops"

   [hosts.meu-host]
   address         = "10.0.0.10"
   description     = "Descrição do host"
   capability_sets = ["system", "storage", "systemd", "network"]
   ```

5. Se algum host precisa de mutação (`mode = "operator"` ou acima) com sudo, gere o
   sudoers a partir do catálogo em vez de escrever à mão:

   ```bash
   ol-ops-mcp emit-sudoers --host meu-host --output /etc/sudoers.d/ol-ops-mcp
   ```

### Variáveis de ambiente

Só estas três têm efeito — nenhuma outra é lida pelo servidor:

| Variável | Efeito |
|----------|--------|
| `OL_OPS_MCP_CONFIG` | Caminho do `hosts.toml`. Default: `~/.config/ol-ops-mcp/hosts.toml`. |
| `OL_OPS_MCP_ALLOW_RAW` | Precisa ser `1` para `ol_raw_exec` funcionar, mesmo em host com `allow_raw = true`. Ver `SECURITY.md`. |
| `OL_OPS_MCP_LOG_LEVEL` | Nível de log do processo (`DEBUG`, `INFO`, `WARNING`, ...). |

## Rodando o servidor

Por padrão, sem argumentos, o processo sobe o servidor MCP via stdio:

```bash
ol-ops-mcp
```

Existem também subcomandos de CLI que **não** sobem o servidor:

```bash
# consulta o audit log local
ol-ops-mcp audit --host meu-host --since 24h --format table

# gera o bloco de sudoers a partir da config atual de um host
ol-ops-mcp emit-sudoers --host meu-host

# compara o sudoers instalado no host remoto com o que a config geraria
ol-ops-mcp verify --host meu-host
```

## Conectando ao Claude Code / Claude Desktop

Ver [`docs/claude-integration.md`](docs/claude-integration.md) para o passo a passo
completo (`claude mcp add`, configuração equivalente no Claude Desktop e como confirmar
que a conexão subiu).

## Tools disponíveis

| Tool | O que faz |
|------|-----------|
| `ol_list_hosts` | Lista hosts configurados: endereço, modo, capability_sets. |
| `ol_list_capabilities` | Lista capabilities do catálogo, opcionalmente por domínio. |
| `ol_describe_capability` | Detalha uma capability: template, parâmetros, risco, timeout. |
| `ol_run_capability` | Executa uma capability. `safe` executa direto; risco maior devolve `PlanResponse` para confirmar com `ol_confirm`. |
| `ol_plan` / `ol_confirm` | Two-phase commit explícito para qualquer capability, mesmo `safe`. |
| `ol_describe_host` | Retrato agregado de um host (release, kernel, memória, disco, unidades com falha, interfaces) numa chamada. |
| `ol_fan_out` | Executa a mesma capability `safe` em vários hosts, concorrentemente. Nunca aceita capability de risco acima de `safe`. |
| `ol_audit_query` | Consulta o audit log local por host, capability, janela de tempo ou risco mínimo. |
| `ol_fetch_file` | Lê um arquivo de texto via SFTP, restrito a `allow_read_paths`. |
| `ol_tail_log` | Últimas linhas de log — de uma unit systemd (`journalctl`) ou de um arquivo (`tail`). |
| `ol_raw_exec` | Escape hatch gated — comando arbitrário, sempre two-phase, sempre com `justification`. Ver `SECURITY.md` antes de habilitar. |

## Exemplo de uso

Depois de conectado, um fluxo típico de diagnóstico via um cliente MCP:

```
> ol_list_hosts
[{"name": "meu-host", "mode": "readonly", ...}]

> ol_describe_host(host="meu-host")
{"release": "Oracle Linux Server 8.10", "disk": [...], "failed_units": [], ...}

> ol_run_capability(host="meu-host", capability="storage.disk_usage")
{"parsed": [{"filesystem": "/dev/sda1", "use_pct": 78, ...}], ...}
```

Para uma mutação (ex. reiniciar um serviço na allowlist do host):

```
> ol_plan(host="meu-host", capability="systemd.restart", params={"service": "mariadb"})
{"rendered_command": "sudo /usr/bin/systemctl restart mariadb", "confirm_token": "...", "expires_at": "..."}

> ol_confirm(confirm_token="...")
{"exit_code": 0, ...}
```

O comando renderizado aparece por completo antes de qualquer execução — o token expira
em 120 segundos e só pode ser usado uma vez.

## Avisos

- **Leia `SECURITY.md` antes de usar em produção.** Em particular: este servidor não é
  um sandbox à prova de operador malicioso — ele protege contra prompt injection e erro
  de julgamento do modelo, não contra alguém que configure deliberadamente
  `mode = "raw"` com sudo irrestrito.
- Hosts de produção devem começar em `mode = "readonly"`. Escalar para `operator` ou
  `raw` é ato explícito no `hosts.toml`, nunca default.
- Nunca coloque senha em texto claro na configuração — só `key_file` é aceito.
- Redaction de segredos na saída remota (`core/redact.py`) é heurística, não garantia
  criptográfica. Trate qualquer stdout/stderr retornado como dado potencialmente
  sensível até prova em contrário.
- Este projeto cobre diagnóstico e operação do sistema operacional. Backup/recovery, HA,
  auditoria de segurança avançada e tuning dentro do banco estão fora de escopo — ver
  `ARCHITECTURE.md` seção 1.3.

## Licença

Apache-2.0.