ol-ops-mcp
by Kevenny
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.
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues