odoo-sh-readonly-mcp
by ai-zar
README.md
# odoo-sh-readonly-mcp 🔒
MCP **solo lectura** para Odoo.sh: builds, entornos, logs y estado del sistema — consultable por una AI sin entrar a la web.
**Sin tools de escritura**: no hay `git push`, ni shell arbitrario, ni escritura de archivos. SSH via `execFile` (sin shell → sin command injection) con comandos whitelisted. El smoke test de CI **falla el build** si alguna vez aparece una tool con nombre de escritura o sin `readOnlyHint`.
---
## 📑 Tabla de contenidos
- [Tools](#-tools)
- [Instalación via imagen GHCR](#-instalación-via-imagen-ghcr-recomendado)
- [Instalación local (Node)](#-instalación-local-node)
- [Configuración](#-configuración)
- [Ejemplos de uso](#-ejemplos-de-uso-con-ai)
- [Nota técnica](#-nota-técnica-modelos-paas)
- [Seguridad](#-seguridad)
---
## 🧰 Tools
| Tool | Fuente | Qué da |
|---|---|---|
| `odoo_sh_overview` | Web API | Repos + branches con stage (production/staging/dev) y último build |
| `odoo_sh_list_builds` | Web API | Builds recientes con **status real** (success/failed/testing) |
| `odoo_sh_model_fields` | Web API | Introspección de campos de modelos `paas.*` |
| `odoo_sh_search_read` | Web API | Query genérica read-only (whitelist `paas.*`) |
| `odoo_sh_logs` | SSH | `tail` de odoo.log / install.log / pip.log (+ filtro) |
| `odoo_sh_system_info` | SSH | Hostname, uptime, disco, memoria, versiones |
| `odoo_sh_databases` | SSH | DBs PostgreSQL y tamaños |
| `odoo_sh_status` | — | Diagnóstico de configuración (sin exponer secretos) |
---
## 🐳 Instalación via imagen GHCR (recomendado)
Cada push a `main` publica una imagen multi-arch (amd64/arm64) en GitHub Container Registry:
```
ghcr.io/ai-zar/odoo-sh-readonly-mcp:latest
```
### 1. Autenticarse contra GHCR
El repo es privado, así que la imagen también. Necesitás un PAT con scope `read:packages`:
```bash
echo $GITHUB_PAT | docker login ghcr.io -u TU_USUARIO --password-stdin
docker pull ghcr.io/ai-zar/odoo-sh-readonly-mcp:latest
```
### 2. Configurar el cliente MCP
**Solo builds/entornos** (sin SSH):
```json
{
"mcpServers": {
"odoo-sh": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "ODOO_SH_SESSION_ID",
"-e", "ODOO_SH_PROJECT",
"ghcr.io/ai-zar/odoo-sh-readonly-mcp:latest"
],
"env": {
"ODOO_SH_SESSION_ID": "tu_session_id",
"ODOO_SH_PROJECT": "mi-proyecto"
}
}
}
}
```
**Con SSH** (logs / system / DBs) — montá la clave privada read-only:
```json
{
"mcpServers": {
"odoo-sh": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-v", "/home/usuario/.ssh/odoo_sh:/keys/id:ro",
"-e", "ODOO_SH_SESSION_ID",
"-e", "ODOO_SH_SSH_HOST",
"-e", "ODOO_SH_SSH_USER",
"-e", "ODOO_SH_SSH_KEY_PATH",
"ghcr.io/ai-zar/odoo-sh-readonly-mcp:latest"
],
"env": {
"ODOO_SH_SESSION_ID": "tu_session_id",
"ODOO_SH_SSH_HOST": "mi-proyecto.dev.odoo.com",
"ODOO_SH_SSH_USER": "BUILD_ID",
"ODOO_SH_SSH_KEY_PATH": "/keys/id"
}
}
}
}
```
> ⚠️ En Windows usá rutas estilo `C:\\Users\\usuario\\.ssh\\odoo_sh:/keys/id:ro` en el `-v`.
> `ODOO_SH_SSH_KEY_PATH` siempre apunta a la ruta **dentro** del contenedor (`/keys/id`).
### 3. Probar la imagen a mano
```bash
docker run -i --rm -e ODOO_SH_SESSION_ID=xxx \
ghcr.io/ai-zar/odoo-sh-readonly-mcp:latest
```
Pegá esto en stdin para ver las tools:
```json
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"cli","version":"1"}}}
{"jsonrpc":"2.0","id":2,"method":"tools/list"}
```
---
## 💻 Instalación local (Node)
```bash
git clone https://github.com/ai-zar/odoo-sh-readonly-mcp.git
cd odoo-sh-readonly-mcp
npm install
npm run smoke # verifica que arranca y expone las 8 tools
```
```json
{
"mcpServers": {
"odoo-sh": {
"command": "node",
"args": ["C:\\ruta\\absoluta\\odoo-sh-readonly-mcp\\index.js"],
"env": { "ODOO_SH_SESSION_ID": "tu_session_id" }
}
}
}
```
---
## ⚙️ Configuración
| Variable | Requerida | Descripción |
|---|---|---|
| `ODOO_SH_SESSION_ID` | ✅ (web) | Cookie `session_id` de www.odoo.sh |
| `ODOO_SH_PROJECT` | ❌ | Repo por defecto para filtrar |
| `ODOO_SH_BASE_URL` | ❌ | Default `https://www.odoo.sh` |
| `ODOO_SH_SSH_HOST` | ✅ (ssh) | `<proyecto>.dev.odoo.com` |
| `ODOO_SH_SSH_USER` | ✅ (ssh) | `BUILD_ID` o nombre de branch |
| `ODOO_SH_SSH_KEY_PATH` | ✅ (ssh) | Ruta a la clave privada |
| `ODOO_SH_SSH_PORT` | ❌ | Default `22` |
| `ODOO_SH_SSH_TIMEOUT_MS` | ❌ | Default `30000` |
### 🍪 Obtener la cookie de sesión
Odoo.sh **no expone API pública**; su panel web es un Odoo estándar. Se usa tu propia sesión:
1. Logueate en <https://www.odoo.sh>
2. F12 → Application → Cookies → `www.odoo.sh` → copiá `session_id`
La cookie expira: cuando las llamadas fallen con error de sesión, repetí el paso. Los permisos son exactamente los de tu usuario — ni más ni menos.
---
## 💬 Ejemplos de uso con AI
- "¿Cómo están los builds de staging?"
- "¿Falló algún build esta semana en main?"
- "Mostrame los últimos errores del log de producción"
- "¿Cuánto pesa la DB de producción?"
- "Listame todos los entornos y en qué commit está cada uno"
---
## 🔬 Nota técnica: modelos `paas.*`
Los modelos internos de Odoo.sh (`paas.build`, `paas.branch`, ...) no están documentados y sus campos pueden cambiar. Por eso:
- **`resilientSearchRead`**: si un campo no existe, reintenta pidiendo todos los campos legibles
- **`odoo_sh_model_fields`**: la AI puede autodescubrir el schema y después consultar con precisión via `odoo_sh_search_read`
Primera vez, pedile a la AI:
> *"Usá `odoo_sh_model_fields` sobre `paas.build` y después listá los builds con los campos correctos"*
---
## 🛡️ Seguridad
| Riesgo | Mitigación |
|---|---|
| Command injection | SSH via `execFile` (argv, sin shell). Comandos remotos son templates fijos; solo enteros validados y un filtro con whitelist de caracteres se interpolan |
| Escritura accidental | No existen tools de escritura. Whitelist de modelos + de métodos (`search_read`, `read`, `search_count`, `fields_get`, `name_search`) |
| Prompt injection → acción destructiva | Superficie de ataque = solo lectura. Lo peor que puede pasar es leer datos a los que ya tenés acceso |
| MITM en SSH | `StrictHostKeyChecking=accept-new` (TOFU) en vez de `no` |
| Secretos en la imagen | Nada hardcodeado; todo por env vars. Clave SSH se monta read-only en runtime |
| Supply chain | Imagen con attestation de provenance (`actions/attest-build-provenance`) firmada por GitHub |
---
MIT — ver [LICENSE](LICENSE).
TDQS
A3.9/5.0
Scored across 8 tools
Disambiguation4/5
Tools are mostly distinct with clear purposes, though overview and list_builds overlap slightly in build-related information. Overall, each tool targets a specific function and confusion is unlikely.
Naming Consistency5/5
All tools use the consistent 'odoo_sh_' prefix followed by snake_case verbs/nouns, creating a uniform and predictable naming pattern.
Tool Count4/5
8 tools is a reasonable number for a read-only Odoo.sh management surface, covering key areas without being excessive or sparse.
Completeness4/5
The set covers critical read-only operations like builds, logs, system info, databases, and model search. It lacks some advanced operations (e.g., deployment history) but for a read-only utility it is well-rounded.
Maintenance
ActivitySlowing
ResponsivenessNo issues