pg-analytics-mcp
pg-analytics-mcp
一个配置驱动、只读的 Postgres MCP 服务器,专为 Claude 设计。将 Postgres 模式通过 Streamable HTTP 暴露给 Claude,模式与枚举值 在启动时从实时数据库内省获取,所有客户端特定配置集中在一个 YAML 文件里。
专为在 Cloudflare Access 之后、运行在 cloudflared → 反向代理
架构上而设计(附带了完整的配置手册),但服务器本身不依赖 Cloudflare,可在任何地方运行。
与客户端无关。 server/ 下的任何代码都不知道任何特定客户端的存在。
要服务新客户端:复制仓库、编写配置文件、设置 .env。
为什么会有这个项目
前身方案为了绕开一个供应商包,堆叠了三个进程:
supergateway → enrich.py → postgres-mcp → Postgrespostgres-mcp 只支持 stdio/SSE(Cloudflare 要求 Streamable HTTP),
完全没有配置面,而 supergateway 为每个 MCP 会话派生一个从未回收的子进程 ——
实测 23 个子进程 / 15 个连接,而角色上限是 20,表现为"大约调用 9 次后一切失败,
连 SELECT 1 都不行"。
本服务器是一个进程,使用一个共享连接池。实测:30 次工具调用后仍是 1 个进程。
Related MCP server: mcp-postgres
架构
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.* views安全边界是数据库角色,而不是这个服务器。
快速开始
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 boot然后按照 docs/PLAYBOOK-NEW-CLIENT.md 处理 Cloudflare 侧。
配置
.env —— 与主机相关,是不同 VPS 之间唯一变化的部分:
变量 | 用途 |
| 只读角色。在 Supavisor 池化器上,用户名必须携带 |
| 容器、镜像标签和 traefik 路由名称 |
| 公开主机名;自动加入传输安全允许列表 |
| traefik 监听的外部 docker 网络 |
| 镜像内客户端 YAML 的路径 |
| 主机侧发布端口(默认 8000) |
config/<client>.yaml —— 领域配置。不要在此列出列或枚举值:
它们在启动时从实时数据库内省获取,因此不会过期。
只写内省无法获知的内容——业务含义与陷阱。
工具
内置工具:
execute_sql(sql)—— 原始只读 SQL。其描述在启动时由你撰写的说明 加上 生成的模式与枚举列表组装而成。list_views()—— 所有可读对象,含列、行数、枚举。describe_view(name)—— 单个对象的列。
配置定义:tools.queries 下的每个条目都会变成真正的 MCP 工具,带类型化参数。参数通过 psycopg 命名占位符绑定——绝不使用字符串插值——并且 min/max 在绑定前强制校验。
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)这正是拉近与 n8n 差距的关键:添加一个工具就是写说明文字 + SQL,而非 Python。
为什么描述放在这里
工具描述是模型在工具可用时所能看到的唯一上下文——每个客户端、每段对话都如此,无需技能加载,也无需项目说明。放在外部文档里的领域知识,往往是模型并不具备的知识。
每段描述一半是撰写的(判断),一半是生成的(事实)。生成的那一半,正是 boxy 平台和 daily 频率不会再像之前手写提示词那样丢失的原因。
运维
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 edit配置或模式变更需要重启 —— 内省结果会在进程生命周期内缓存,这是刻意为之, 这样行为就不会在运行中途漂移。
五项边界测试
在对视图、授权或配置做任何修改后重新运行。五项必须全部失败:
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 默认关闭:角色才是边界,而受限模式会拦截合法的只读构造,却毫无收益 —— 这正是 postgres-mcp 的受限模式被放弃的原因。
血泪教训
Compose 的标签键不做变量替换。 标签必须使用列表形式 (
- "traefik...=value"),否则你会得到一个名字恰恰是${MCP_CONTAINER_NAME}的路由器,然后 traefik 返回 404。MCP SDK 默认开启 DNS 重绑定保护。 代理后转发的
Host必须被允许;MCP_HOSTNAME和MCP_LOCAL_PORT会 自动添加。将 MCP 应用挂载到你自己的 Starlette 下会覆盖生命周期。 必须显式启动会话管理器(
server.session_manager.run()),否则 每个请求都会以 "Task group is not initialized" 报 500 错误。set_read_only/set_autocommit必须在任何execute()之前调用, 否则连接池会报 "connection in transaction status INTRANS"。pg_class.reltuples对视图毫无意义,因此行数估算在启动时 回退为有界count(*)。Supavisor 会把
application_name改写成 "Supavisor",因此无法通过池化器做按客户端连接归属。MCP SDK 2.0 将
FastMCP重命名为MCPServer并移出了mcp.server.fastmcp。requirements.txt因此是完整锁定版本。
许可证
MIT —— 见 LICENSE。
This server cannot be deployed
Maintenance
Related MCP Connectors
Query your warehouse or a CSV with Claude/ChatGPT over MCP, governed by table-level ACL + audit.
Read-only analytics for Convex apps, queryable via MCP from Claude, Cursor, and other clients.
Text-to-SQL MCP server: read-only queries on PostgreSQL, MySQL and SQL Server with your real schema
- dataOAuthco.thinair
PostgreSQL, MySQL, and SQL Server in one session. 26 read-only MCP tools for AI agents.
Related MCP Servers
- AlicenseAqualityBmaintenanceExposes PostgreSQL query execution, EXPLAIN, and schema inspection tools to MCP-compatible clients like Claude Desktop.31MIT
- FlicenseNot gradedqualityDmaintenanceConnects to a PostgreSQL database and exposes schema inspection and safe SELECT query execution as MCP tools and resources for use with Claude Desktop or any MCP-compatible client.-
- AlicenseNot gradedqualityCmaintenanceEnables read-only, SELECT-only querying of any Postgres database through MCP-compatible clients like Claude, with schema introspection and guarded SQL execution.MIT
- AlicenseAqualityBmaintenanceEnables Claude Desktop and Claude Code to inspect a Postgres schema and run read-only SQL queries against a demo database through the tools describe_schema and query_readonly. Every proposed query passes the same deterministic guardrails — SELECT-only, allow-listed tables, enforced row limits, statement timeouts and a read-only role — before execution.2MIT