Skip to main content
Glama
cuevacelis

MCP PostgreSQL

by cuevacelis
README.md
# MCP PostgreSQL

Servidor [Model Context Protocol](https://modelcontextprotocol.io) que expone introspección y consulta **read-only** sobre PostgreSQL. Diseñado para alimentar de contexto a Claude (Claude Code, Claude Desktop) sin riesgo de escritura.

- **13 tools** de introspección, consulta, EXPLAIN y estadísticas de almacenamiento
- **2 resources** navegables (`postgres://schema/{schema}`, `postgres://table/{schema}/{table}`)
- **4 prompts** listos para usar (`audit-table`, `find-tables`, `explain-foreign-keys`, `profile-slow-query`)
- **Read-only reforzado**: `SET TRANSACTION READ ONLY`, statement timeout, cap de filas, single-statement, validación de keywords
- **Schema allow-list** vía `DB_SCHEMAS`
- Empaquetable como **MCPB** para Claude Desktop (`pnpm run mcpb:pack`)

---

## Tabla de contenidos

- [Quickstart](#quickstart)
- [Configuración](#configuración)
- [Integración con Claude Code](#integración-con-claude-code)
- [Integración con Claude Desktop (MCPB)](#integración-con-claude-desktop-mcpb)
- [Tools](#tools)
- [Resources](#resources)
- [Prompts](#prompts)
- [Seguridad](#seguridad)
- [Estructura del proyecto](#estructura-del-proyecto)
- [Desarrollo](#desarrollo)
- [Troubleshooting](#troubleshooting)

---

## Quickstart

Requisitos: Node ≥ 18 y `pnpm`.

```bash
pnpm install
pnpm run build
cp .env.example .env       # edita tus credenciales
npx tsx test-connection.ts # verifica conectividad
```

Luego registra el servidor en Claude Code:

```bash
claude mcp add --transport stdio postgres \
  -- node /ruta/absoluta/al/proyecto/dist/index.js
```

## Configuración

Copia `.env.example` a `.env` y edita los valores:

```env
DB_HOST=localhost
DB_PORT=5432
DB_NAME=nombre_base_datos
DB_USER=usuario
DB_PASSWORD=contraseña
DB_SSL=false                       # true para AWS RDS / Supabase / Neon
DB_SSL_REJECT_UNAUTHORIZED=true    # mantener true en producción
DB_SCHEMAS=public                  # esquemas permitidos, separados por coma. Vacío = todos los no-sistema
DEFAULT_LIMIT=5                    # LIMIT por defecto en queries (máx 100)
```

### Entornos preconfigurados

Hay varios archivos `.env.<entorno>` para alternar entre bases de datos sin reescribir credenciales:

```bash
cp .env.ecosistema-prd .env       # producción
cp .env.ecosistema-tst .env       # testing
cp .env.db-admision-tst .env      # admisión testing
# ...etc.
```

> **Recomendación**: usa un **rol de PostgreSQL de solo lectura** (`CREATE ROLE ... LOGIN; GRANT USAGE ON SCHEMA ... TO ...; GRANT SELECT ON ALL TABLES IN SCHEMA ... TO ...;`). El servidor refuerza READ ONLY, pero la defensa en profundidad importa.

## Integración con Claude Code

### Opción A — `claude mcp add` (recomendado)

Pasando credenciales como variables de entorno:

```bash
claude mcp add \
  --transport stdio \
  --env DB_HOST=localhost \
  --env DB_PORT=5432 \
  --env DB_NAME=mi_base \
  --env DB_USER=mi_user \
  --env DB_PASSWORD=mi_password \
  --env DB_SCHEMAS=public \
  postgres \
  -- node /ruta/absoluta/al/proyecto/dist/index.js
```

Tomando el `.env` del propio repo (omite los `--env`):

```bash
claude mcp add --transport stdio postgres \
  -- node /ruta/absoluta/al/proyecto/dist/index.js
```

Scope global (disponible en todos los proyectos):

```bash
claude mcp add --scope user --transport stdio postgres \
  -- node /ruta/absoluta/al/proyecto/dist/index.js
```

### Opción B — JSON manual

```json
{
  "mcpServers": {
    "postgres": {
      "command": "node",
      "args": ["/ruta/absoluta/al/proyecto/dist/index.js"],
      "env": {
        "DB_HOST": "...",
        "DB_NAME": "...",
        "DB_USER": "...",
        "DB_PASSWORD": "...",
        "DB_SCHEMAS": "public"
      }
    }
  }
}
```

## Integración con Claude Desktop (MCPB)

El proyecto incluye un `manifest.json` listo para empaquetar como [MCPB](https://modelcontextprotocol.io) (Claude Desktop Bundle).

```bash
pnpm run build
pnpm run mcpb:pack    # genera mcp_postgres.mcpb en la raíz del repo
```

El `.mcpb` es un **artefacto de build** (está en `.gitignore`) — se regenera cuando lo necesites. No lo subas al repo.

### Qué hacer con `mcp_postgres.mcpb`

**Uso personal (instalarlo en tu Claude Desktop):**

1. Abre Claude Desktop → **Settings** → **Extensions**
2. Arrastra `mcp_postgres.mcpb` a la ventana (o usa "Install extension")
3. Claude Desktop te pedirá los datos de conexión (host, user, password, etc.) mediante el formulario definido en `user_config` del `manifest.json` — el campo `db_password` está marcado como `sensitive`
4. Una vez instalado, puedes borrar el archivo `.mcpb` local

**Distribución privada (compartir con tu equipo):**

- Súbelo como release asset en GitHub: `gh release create v1.1.0 mcp_postgres.mcpb`
- O distribúyelo por un storage interno (S3, Drive, etc.) y comparte el link
- Tus compañeros descargan el `.mcpb` y lo arrastran a Claude Desktop

**Distribución pública:** publícalo en el MCP Bundle Directory cuando esté disponible. Mientras tanto, GitHub Releases es el canal estándar.

**Si no lo vas a instalar ahora:** simplemente bórralo (`rm mcp_postgres.mcpb`) y regenéralo con `pnpm run mcpb:pack` cuando lo necesites.

## Tools

Todas las tools llevan `readOnlyHint: true`, `destructiveHint: false` y `outputSchema` Zod para `structuredContent`. Los errores recuperables se devuelven como `{ isError: true, content }`, no como excepciones de protocolo.

### Introspección

| Tool | Descripción |
|------|-------------|
| `postgres_list_schemas` | Lista esquemas accesibles. |
| `postgres_list_tables` | Tablas de un esquema con conteo de columnas (paginado). |
| `postgres_describe_table` | Columnas, constraints (PK/FK/UNIQUE) e índices. |
| `postgres_list_functions` | Funciones/procedimientos con firma, retorno y lenguaje (paginado). |
| `postgres_list_triggers` | Triggers con tabla, evento y timing (paginado). |
| `postgres_get_function_definition` | Código fuente de una función (con soporte de sobrecarga). |
| `postgres_get_trigger_definition` | Definición completa de un trigger. |
| `postgres_list_views` | Vistas regulares y materializadas (paginado). |
| `postgres_search_columns` | Busca columnas por nombre/patrón en todos los esquemas permitidos. |

### Consulta y análisis

| Tool | Descripción |
|------|-------------|
| `postgres_query_table` | SELECT seguro sobre una sola tabla con filtros estructurados. |
| `postgres_execute_query` | SELECT/WITH avanzado (JOINs, CTEs, agregaciones). Single-statement, READ ONLY. |
| `postgres_explain_query` | EXPLAIN / EXPLAIN ANALYZE de un SELECT — perfila planes antes de ejecutar. |
| `postgres_get_table_stats` | Tamaño total/tabla/índices/toast, vacuum/analyze, índices con `idx_scan = 0`. |

Las tools paginadas (`postgres_list_*`) aceptan `limit`/`offset` y devuelven `has_more`/`next_offset` para iterar.

## Resources

URIs navegables que el host puede consumir como contexto:

| URI template | Contenido |
|--------------|-----------|
| `postgres://schema/{schema}` | Resumen del esquema: tablas, vistas, funciones, triggers, conteos. |
| `postgres://table/{schema}/{table}` | Estructura completa de una tabla (columnas + constraints + índices). |

## Prompts

Slash commands disponibles en Claude Code (`/mcp__postgres__<prompt>`):

| Prompt | Argumentos | Propósito |
|--------|------------|-----------|
| `audit-table` | `schema`, `table` | Auditoría estructurada: schema, storage, sample, triggers, riesgos. |
| `find-tables` | `pattern` | Encuentra columnas/tablas por patrón fuzzy en todos los esquemas. |
| `explain-foreign-keys` | `schema` | Mapa textual del grafo de FKs (hubs, huérfanos). |
| `profile-slow-query` | `sql` | EXPLAIN ANALYZE + recomendaciones priorizadas (índices faltantes, scans, sorts caros). |

Ejemplo (en Claude Code):

```
/mcp__postgres__audit-table schema=public table=users
/mcp__postgres__profile-slow-query sql="SELECT * FROM orders WHERE customer_id IN (SELECT id FROM customers WHERE country='PE')"
```

## Seguridad

- **Filtrado de esquemas**: `DB_SCHEMAS` restringe acceso a nivel de aplicación; además `postgres_execute_query` ajusta `search_path` local por consulta.
- **Query table seguro**: `postgres_query_table` usa columnas/filtros estructurados con parámetros SQL — nunca concatena strings.
- **Read-only reforzado**: `postgres_execute_query` y `postgres_explain_query` corren bajo `SET TRANSACTION READ ONLY` con `statement_timeout = 30s`.
- **Single-statement**: rechaza queries con `;` interno y palabras clave de escritura (INSERT/UPDATE/DELETE/DROP/ALTER/CREATE/TRUNCATE/GRANT/REVOKE/EXECUTE/COPY) validadas por regex con `\b`.
- **Cap de filas**: máximo 100 filas por consulta; se inyecta o clampa el `LIMIT` automáticamente.
- **TLS seguro por defecto**: cuando `DB_SSL=true`, `DB_SSL_REJECT_UNAUTHORIZED=true` por defecto.
- **Timeouts**: 10 s para establecer conexión, 30 s para statement.
- **Defensa en profundidad**: aun así, **usa un rol de PostgreSQL de solo lectura** en el `DB_USER`.

## Estructura del proyecto

```
src/
├── index.ts              # Bootstrap — conecta transport y verifica DB
├── server.ts             # createServer() — instancia McpServer, registra tools/resources/prompts
├── db/
│   └── pool.ts           # Pool, allowedSchemas, defaultLimit, isSchemaAllowed
├── tools/
│   ├── introspection.ts  # list_schemas / list_tables / describe_table / list_views / search_columns
│   ├── objects.ts        # list_functions / list_triggers / get_*_definition
│   ├── query.ts          # query_table / execute_query
│   └── analysis.ts       # explain_query / get_table_stats
├── resources/
│   └── index.ts          # postgres://schema/* y postgres://table/*
├── prompts/
│   └── index.ts          # audit-table / find-tables / explain-foreign-keys / profile-slow-query
└── utils/
    └── response.ts       # formatResult(), assertSchemaAllowed(), CHARACTER_LIMIT
```

## Desarrollo

```bash
pnpm run build      # Compila TypeScript → dist/
pnpm run dev        # Watch mode
pnpm start          # Ejecuta el servidor compilado
pnpm run mcpb:pack  # Empaqueta como .mcpb para Claude Desktop
```

Tras editar cualquier archivo en `src/`, ejecuta `pnpm run build` antes de probar cambios. El binario que Claude Code/Desktop lanza es `dist/index.js`.

## Troubleshooting

**`Error: schema "X" is not allowed`** — añade `X` a `DB_SCHEMAS` (o déjalo vacío para permitir todos los no-sistema).

**`statement timeout`** — la query supera 30 s. Usa `postgres_explain_query` con `analyze=false` primero, o filtra por una columna indexada.

**`self-signed certificate` en RDS / Supabase** — establece `DB_SSL=true`. Solo baja `DB_SSL_REJECT_UNAUTHORIZED=false` si el proveedor usa cert auto-firmado.

**Claude Code no ve las tools** — verifica con `claude mcp list` que `postgres` aparece como `connected`. Si no, `claude mcp get postgres` te muestra el comando registrado; comprueba que la ruta absoluta a `dist/index.js` es correcta y que el build está actualizado.

**Query devuelve `has_more: true`** — vuelve a llamar la tool pasando `offset = next_offset`. Las listas están paginadas para no inflar el contexto.

## Licencia

MIT

Maintenance

ActivityInactive
ResponsivenessNo issues