Skip to main content
Glama
gilberth
by gilberth
README.md
# proxmox-docs-mcp

Servidor MCP de **solo lectura** que indexa la guía oficial de administración de Proxmox VE
y la expone como herramientas y recursos MCP.

- Fuente canónica única: `https://pve.proxmox.com/pve-docs/pve-admin-guide.html`
- El servidor **no** responde con conocimiento propio: solo devuelve fragmentos recuperables
  de la guía, cada uno con su URL oficial y ancla, más metadatos de frescura.

## Requisitos

- Node.js >= 22
- pnpm (este proyecto usa exclusivamente pnpm; no uses npm ni yarn)

## Instalación

```bash
pnpm install
```

`better-sqlite3` se compila de forma nativa durante la instalación.

## Configuración

Todas las variables tienen valores por defecto seguros (ver `.env.example`):

| Variable | Predeterminado | Descripción |
|---|---|---|
| `PROXMOX_DOC_URL` | URL oficial | Debe usar `https://pve.proxmox.com`. |
| `PROXMOX_DOC_DATA_DIR` | `.data` | Directorio persistente de SQLite y metadatos. |
| `PROXMOX_DOC_REFRESH_TTL_SECONDS` | `21600` | TTL antes de revalidar la fuente (6 h). |
| `PROXMOX_DOC_HTTP_TIMEOUT_MS` | `30000` | Timeout de descarga. |
| `PROXMOX_DOC_MAX_DOWNLOAD_BYTES` | `10000000` | Límite defensivo de descarga. |
| `MCP_TRANSPORT` | `stdio` | `stdio` o `http`. |
| `MCP_HTTP_HOST` | `127.0.0.1` | Host de escucha del transporte HTTP. |
| `MCP_HTTP_PORT` | `8093` | Puerto del transporte HTTP. |
| `MCP_HTTP_PATH` | `/mcp` | Ruta del endpoint MCP. |
| `LOG_LEVEL` | `info` | `debug` \| `info` \| `warn` \| `error`. |

La configuración se valida al inicio y falla con mensajes específicos ante valores inválidos.

## Uso local

### stdio (clientes de escritorio)

Configuración de cliente MCP:

```json
{
  "mcpServers": {
    "proxmox-docs": {
      "command": "pnpm",
      "args": ["--dir", "/ruta/absoluta/al/proyecto", "dev:stdio"]
    }
  }
}
```

`stdout` queda reservado para mensajes MCP; todos los logs van a `stderr`.

### Streamable HTTP (local)

```bash
pnpm dev:http
# endpoint MCP: http://127.0.0.1:8093/mcp
# health check:  http://127.0.0.1:8093/healthz
```

El servidor HTTP escucha solo en `127.0.0.1` y valida `Host` y `Origin` antes de
entregar la petición al handler MCP.

### Inspector

```bash
pnpm dlx @modelcontextprotocol/inspector pnpm dev:stdio
```

Para HTTP, arranca `pnpm dev:http` y conecta el Inspector a `http://127.0.0.1:8093/mcp`.

## Contrato MCP

### Herramientas

| Herramienta | Descripción |
|---|---|
| `search_proxmox_docs` | Busca fragmentos en la guía (`query`, `limit` ≤ 10, `sectionPrefix` opcional). La guía está en inglés: traduce las consultas a términos técnicos en inglés conservando comandos, rutas y opciones literales. |
| `get_proxmox_section` | Lee una sección localizada por la búsqueda (`sectionId`, `cursor` opcional, `maxChars` ≤ 20000). Devuelve `nextCursor` si queda contenido. |
| `get_proxmox_doc_status` | Informa URL canónica, `ETag`, `Last-Modified`, fechas de comprobación y de última sincronización correcta, conteo de secciones/fragmentos y si la copia está vencida. |
| `refresh_proxmox_docs` | Fuerza una comprobación condicional inmediata: `not_modified`, `updated`, `stale_fallback` o `failed_no_cache`. No modifica Proxmox. |

Todas las herramientas se anotan como no destructivas y de solo lectura
(`refresh_proxmox_docs` añade `openWorldHint` por hacer una petición de red).

### Recursos

- `proxmox-docs://status` — estado y metadatos de la fuente (`application/json`).
- `proxmox-docs://section/{sectionId}` — lectura direccionable de una sección (`application/json`).

## Frescura y modo vencido (`stale`)

- El TTL predeterminado es 6 horas. En el primer arranque, el servidor descarga e indexa
  una copia válida antes de quedar listo.
- `304 Not Modified` se trata como sincronización correcta y no reconstruye el índice.
- Si una actualización falla (timeout, error HTTP, HTML inválido, fallo de indexado) y existe
  una copia válida, las respuestas siguen sirviéndose con `stale: true` y la fecha de la
  última sincronización correcta. Una actualización fallida **nunca** reemplaza la última
  copia válida.
- Una instalación sin ninguna copia válida devuelve un error accionable.

## Pruebas

```bash
pnpm test        # suite determinista (sin red)
pnpm typecheck   # tsc --noEmit (tsconfig.json: src + test)
pnpm build       # tsc -p tsconfig.build.json -> dist/src
pnpm test:live   # prueba real contra la guía oficial (requiere red; RUN_LIVE_PROXMOX_TESTS=1)
```

`evals/proxmox-docs.xml` contiene diez preguntas verificables resueltas mediante las
herramientas de recuperación contra un índice real.

## Archivos de datos

`PROXMOX_DOC_DATA_DIR` (por defecto `.data/`) contiene `proxmox-docs.sqlite` con el HTML
almacenado, las secciones, los fragmentos y el índice FTS5. Está en `.gitignore`; el índice
puede reconstruirse desde el HTML almacenado sin volver a descargarlo.

## Límite de seguridad

- Fuente restringida a HTTPS en `pve.proxmox.com`; el HTML se trata como dato no confiable
  (sin scripts, eventos ni contenido embebido).
- Consultas SQLite siempre parametrizadas.
- No hay credenciales de Proxmox: la documentación es pública.
- El servidor HTTP escucha solo en loopback; la publicación remota es una fase separada.

## Despliegue remoto

El despliegue en el LXC 134 vía Cloudflare Tunnel es una fase separada que se ejecuta con el
skill `mcp-deployer` después de validar el servidor localmente. Su primer paso obligatorio es
elegir entre **URL secreta** u **OAuth 2.1 con PocketID**. Nada en LXC 134 se toca hasta esa
elección.