Skip to main content
Glama
Sa3fa

pg-analytics-mcp

by Sa3fa

pg-analytics-mcp

一个配置驱动、只读的 Postgres MCP 服务器,专为 Claude 设计。将 Postgres 模式通过 Streamable HTTP 暴露给 Claude,模式与枚举值 在启动时从实时数据库内省获取,所有客户端特定配置集中在一个 YAML 文件里。

专为在 Cloudflare Access 之后、运行在 cloudflared → 反向代理 架构上而设计(附带了完整的配置手册),但服务器本身不依赖 Cloudflare,可在任何地方运行。

与客户端无关。 server/ 下的任何代码都不知道任何特定客户端的存在。 要服务新客户端:复制仓库、编写配置文件、设置 .env。

为什么会有这个项目

前身方案为了绕开一个供应商包,堆叠了三个进程:

supergateway  →  enrich.py  →  postgres-mcp  →  Postgres

postgres-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 之间唯一变化的部分:

变量

用途

DATABASE_URI

只读角色。在 Supavisor 池化器上,用户名必须携带 .PROJECT_REF。

MCP_CONTAINER_NAME

容器、镜像标签和 traefik 路由名称

MCP_HOSTNAME

公开主机名;自动加入传输安全允许列表

TRAEFIK_NETWORK

traefik 监听的外部 docker 网络

MCP_CONFIG

镜像内客户端 YAML 的路径

MCP_LOCAL_PORT

主机侧发布端口(默认 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 exist

limits.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。

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Connects 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.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables read-only, SELECT-only querying of any Postgres database through MCP-compatible clients like Claude, with schema introspection and guarded SQL execution.
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables 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.
    2
    MIT