pg-analytics-mcp
pg-analytics-mcp
Un servidor MCP de Postgres para Claude, de solo lectura y basado en configuración. Expone un esquema de Postgres a Claude a través de Streamable HTTP, con los valores de esquema y enum introspectados desde la base de datos en vivo al arrancar, y todo lo específico del cliente en un único archivo YAML.
Diseñado para ejecutarse detrás de Cloudflare Access en una pila cloudflared → proxy inverso (se incluye un playbook de aprovisionamiento completo), pero el servidor en sí no tiene dependencia de Cloudflare y se ejecuta en cualquier lugar.
Independiente del cliente. Nada en server/ sabe nada sobre un cliente concreto. Para servir a uno nuevo: copia el repositorio, escribe un archivo de configuración, configura .env.
Por qué existe esto
El predecesor apilaba tres procesos para sortear un paquete de un proveedor:
supergateway → enrich.py → postgres-mcp → Postgrespostgres-mcp solo habla stdio/SSE (Cloudflare exige Streamable HTTP), no tiene ninguna superficie de configuración, y supergateway bifurcaba un hijo por sesión MCP que nunca se recolectaba — se midieron 23 hijos / 15 conexiones frente a un límite de rol de 20, lo que se manifestaba como "funciona durante ~9 llamadas y luego falla todo, incluido SELECT 1".
Este servidor es un solo proceso con un único pool compartido. Medido: 1 proceso después de 30 llamadas a herramientas.
Related MCP server: Brand MCP Server
Arquitectura
Claude → portal.<zone> Cloudflare MCP Server Portal (OAuth)
→ mcp-origin.<zone> Access app + Managed OAuth
→ cloudflared tunnel
→ traefik Host-header routing
→ this container uvicorn, Streamable HTTP at /mcp
→ Postgres read-only role → analytics.* viewsEl límite de seguridad es el rol de la base de datos, no este servidor.
Inicio rápido
cp .env.example .env # set DATABASE_URI + the deployment vars
$EDITOR config/example.yaml # domain prose for this client
docker compose up -d --build
curl -s localhost:8000/healthz # ok
curl -s localhost:8000/introspection # what the server decided at bootA continuación, sigue docs/PLAYBOOK-NEW-CLIENT.md para la parte de Cloudflare.
Configuración
.env — específico del host, lo único que cambia entre VPS:
Variable | Propósito |
| Rol de solo lectura. En el pooler de Supavisor, el nombre de usuario debe llevar |
| Contenedor, etiqueta de imagen y nombre del router de traefik |
| Nombre de host público; se añade automáticamente a la lista de permitidos de seguridad de transporte |
| Red docker externa que traefik observa |
| Ruta al YAML del cliente dentro de la imagen |
| Puerto de publicación en el host (por defecto 8000) |
config/<client>.yaml — el dominio. No enumeres columnas ni valores de enum aquí: se introspectan desde la base de datos en vivo al arrancar, por lo que no pueden quedar obsoletos. Escribe solo lo que la introspección no puede saber: significado de negocio y trampas.
Herramientas
Integradas:
execute_sql(sql)— SQL de solo lectura sin procesar. Su descripción se ensambla al arrancar a partir de tu prosa redactada más los esquemas y listas de enum generados.list_views()— todos los objetos legibles con columnas, recuentos de filas y enums.describe_view(name)— las columnas de un objeto.
Definidos por configuración: cada entrada en tools.queries se convierte en una herramienta MCP real con parámetros tipados. Los parámetros se vinculan mediante placeholders nombrados de psycopg — nunca interpolación de cadenas — y min/max se aplican antes de la vinculación.
tools:
queries:
monthly_trend:
description: |
Donations per month. The most recent month is PARTIAL.
params:
months: {type: integer, default: 6, min: 1, max: 36}
sql: |
select ... where donated_at >= date_trunc('month', now())
- make_interval(months => %(months)s - 1)Esta es la pieza que cierra la brecha con n8n: añadir una herramienta es prosa + SQL, no Python.
Por qué las descripciones viven aquí
Las descripciones de las herramientas son el único contexto que un modelo ve siempre que la herramienta está disponible — en cada cliente, en cada conversación, sin carga de habilidades ni instrucciones de proyecto. El conocimiento de dominio guardado en un documento externo es conocimiento que el modelo a menudo no tiene.
La mitad de cada descripción es redactada (juicio), la mitad generada (hechos). La mitad generada es la razón por la que la plataforma/procesador boxy y la frecuencia daily ya no pueden perderse como ocurría en el prompt escrito a mano que precedió a esto.
Operaciones
curl -s localhost:8000/introspection | python3 -m json.tool # objects, enums, tools, limits
docker top <container> # must stay at 1 process
docker compose up -d --build # after a config editUn cambio de configuración o de esquema requiere un reinicio: la introspección se almacena en caché durante toda la vida del proceso, deliberadamente, para que el comportamiento no pueda desviarse a mitad de ejecución.
Las cinco pruebas de límite
Vuelve a ejecutarlos tras cualquier cambio en vistas, permisos o configuración. Los cinco deben fallar:
update donations set amount = 0 where false; -- permission denied for view
select count(*) from public.donations; -- permission denied for table
select count(*) from public.website_orders; -- permission denied for table
create table analytics.t (id int); -- read-only transaction
select phone_number from customers limit 1; -- column does not existlimits.select_only existe pero está desactivado por defecto: el rol es el límite, y un validador SQL encima bloquea construcciones válidas de solo lectura sin ninguna ventaja — por eso se abandonó el modo restringido de postgres-mcp.
Errores pagados con sangre
Las claves de las etiquetas de Compose no se sustituyen con variables. Las etiquetas deben estar en forma de lista (
- "traefik...=value"), o tendrás un router literalmente llamado${MCP_CONTAINER_NAME}y traefik devolverá 404.La protección contra el rebinding de DNS está activada por defecto en el SDK de MCP. El
Hostreenviado detrás de un proxy debe estar permitido;MCP_HOSTNAMEyMCP_LOCAL_PORTse añaden automáticamente.Montar la aplicación MCP bajo tu propio Starlette reemplaza su ciclo de vida. El gestor de sesiones debe iniciarse explícitamente (
server.session_manager.run()) o cada petición devolverá 500 con "Task group is not initialized".set_read_only/set_autocommitdeben preceder a cualquierexecute()en una conexión, o el pool falla con "connection in transaction status INTRANS".pg_class.reltuplescarece de sentido para las vistas, por lo que las estimaciones de filas recurren a uncount(*)acotado al arrancar.Supavisor reescribe
application_namea "Supavisor", por lo que la atribución de conexiones por cliente a través del pooler no es posible.El SDK de MCP 2.0 renombró
FastMCPaMCPServery lo sacó demcp.server.fastmcp.requirements.txtes un bloqueo completo por esa razón.
Licencia
MIT — consulta LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables Claude to interact with PostgreSQL databases by executing SQL queries, exploring schemas, and monitoring database health. It provides tools for data manipulation and schema management via a secure SSE connection.287MIT
- FlicenseNot gradedqualityDmaintenanceEnables Claude Desktop to query a PostgreSQL brand database through MCP. Supports local stdio and remote HTTP/SSE deployments with API key authentication for secure database access.
- FlicenseNot gradedqualityDmaintenanceEnables natural language querying of PostgreSQL databases through the Model Context Protocol. It translates user questions into validated SQL, executes read-only queries safely, and returns results to MCP-compatible clients like Claude Desktop.
- AlicenseAqualityAmaintenanceQuery and manage PostgreSQL databases from Claude Code, Cursor, and any MCP client, with read-only by default and built-in schema introspection, EXPLAIN, and performance diagnostics.211,8093MIT
Related MCP Connectors
Analytical memory for AI agents: a real Postgres queried in plain English over MCP. One command.
Query PostgreSQL databases in plain English — LLM-generated, safety-validated SQL.
MCP server for managing Prisma Postgres.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Sa3fa/pg-analytics-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server