mcpField
S
sql-steward

sql-steward

本地服务
github.com/Pawansingh3889数据库013 周前更新

关于 sql-steward

sql-steward 是面向需要治理数据库访问的团队的 MCP SQL 网关。它依据运维方定义的语义层编译只读查询,并在读取前拦截带有 PII 标记的字段。

主要功能

  1. 01实体、记录和指标的类型化工具
  2. 02仅编译 SELECT 查询
  3. 03读取前执行 PII 策略检查
  4. 04防篡改审计验证
  5. 05支持 SQL Server、Postgres 和 SQLite

适用场景

  1. 01向 AI Agent 提供获准的指标
  2. 02让 Agent 读取记录而不接触原始 SQL
  3. 03阻止读取受限的 PII 字段
  4. 04验证 Agent 调用的审计链

MCP 相关

它向 MCP 客户端提供类型化数据库工具,不提供原始 SQL 执行工具。查询由配置的语义层编译,策略禁止的 PII 会在执行前被拒绝。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"pawansingh3889-sql-steward": {
"command": "uvx",
"args": [
"sql-steward"
],
"env": {
"SQL_STEWARD_DB_URL": "<SQL_STEWARD_DB_URL>",
"SQL_STEWARD_LAYER": "<SQL_STEWARD_LAYER>"
}
}
}
}
启动命令uvx sql-steward
需要配置的环境变量
SQL_STEWARD_DB_URL密钥SQLAlchemy URL of the database to read (read-only account recommended)
SQL_STEWARD_LAYERPath to the semantic layer YAML (default: semantic.yaml)

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypisql-steward0.4.0

工具

9

收录的工具列表 · 更新于 2 周前

此工具无需参数。

详细介绍

重要

此仓库已迁移至 Governed Agent Stack monorepo。

当前开发工作已转移至 packages/sql-steward/。 此仓库已归档并设为只读。这里保留了完整的提交历史; 新功能、问题和版本发布均在 monorepo 中进行。


sql-steward

PyPI Downloads

License Python Discord

属于 Governed Agent Stack 的一部分:为可连接真实数据库并可审计的 AI Agent 提供免费、可本地部署的构建模块。

通过 Model Context Protocol 向 AI Agent 提供受治理的 SQL 网关。Agent 永远不会获得连接字符串,也不会编写 SQL。它会调用类型化工具;sql-steward 根据你控制的语义层编译每个查询,在查询运行前拒绝涉及受限 PII 的请求,并返回行数据。SQL Server、Postgres 和 SQLite 使用同一套工具。

大多数 SQL MCP 服务会向模型提供 run_sql 工具,然后试图拦截执行过程中出现的恶意查询。sql-steward 则移除了这个工具。提示词无法通过任何路径让你的数据库执行原始 SQL,因为 Agent 唯一能做的就是指定实体或指标,并从你编写的允许列表中进行选择。

实时观看它保护数据库。受治理与不受治理的演示会将同一个请求分别交给一个简单的 run_sql Agent(它会泄露客户 PII 并清空一张表)和 sql-steward(它会在编译时拒绝该请求)。

三项保证

  1. **从设计上只读。**这里没有 run_sql、query 或 execute 工具。编译器只能构建 SELECT,因此写入操作不是被拦截,而是根本无法表达。
  2. **检索前拒绝 PII。**每个字段都可以附带 PII 标签。如果请求涉及你的策略所禁止的类别,sql-steward 会在编译或运行任何 SQL 之前,以结构化原因拒绝请求。
  3. **可审计。**每次调用、拒绝和错误都可以通过 agent-blackbox 记录到防篡改的哈希链日志中,并使用 audit-verify 证明记录未被改写。

10 秒快速体验

pip install sql-steward      # or: pipx install sql-steward
sql-steward demo            # zero config, no API key, no agent, SQLite
1) get_metric('mrr_total', dimensions=['plan'])  -> safe aggregate
   compiled: SELECT subscriptions.plan, SUM(subscriptions.mrr) AS mrr_total
             FROM subscriptions GROUP BY subscriptions.plan LIMIT 1000
   {'plan': 'pro', 'mrr_total': 297.0}
   {'plan': 'team', 'mrr_total': 598.0}

2) get_metric('mrr_total', dimensions=['customers.country'])  -> auto-join
   compiled: ... INNER JOIN customers ON subscriptions.customer_id = customers.id ...

3) get_records('customers', fields=['id','email'])  -> PII refusal
   refused: {"kind": "pii_blocked", "detail": "Field 'customers.email' is tagged
             EMAIL_ADDRESS, which this policy refuses."}

语义层

此 YAML 是 Agent 与你的数据库之间的完整契约。请像审查代码一样审查它。

dialect: postgres

entities:
  customers:
    table: customers
    fields:
      id: {type: int}
      name: {type: text, pii: PERSON}
      email: {type: text, pii: EMAIL_ADDRESS}
      country: {type: text}
  subscriptions:
    table: subscriptions
    fields:
      customer_id: {type: int}
      plan: {type: text}
      mrr: {type: numeric}

joins:                              # nothing reachable that isn't listed here
  - left: subscriptions
    right: customers
    on: subscriptions.customer_id = customers.id

metrics:
  mrr_total:                        # the aggregation is fixed; the agent only
    entity: subscriptions           # chooses dimensions/filters from the lists
    aggregate: sum
    field: mrr
    dimensions_allowed: [plan, status, customers.country]
    filters_allowed: [status, customers.country]

policy:
  block_pii: [EMAIL_ADDRESS, CREDIT_CARD]
  max_rows: 1000

请求未定义的联接时,你会得到 unreachable_entity,而不是虚构的关系。请求按未列出的维度对指标分组时,你会得到 dimension_not_allowed。拼错指标或字段时,错误会列出实际存在的名称及最接近的拼写,因此 Agent 可以在一轮内自行纠正,无需再发起一次发现调用。

向 Agent 暴露的工具

工具用途
list_entities()可读取的内容,以及可用的指标
describe_entity(entity)字段、类型和 PII 标签(会标出被阻止的字段)
list_metrics()指标及每个指标允许使用的维度/筛选条件
get_records(entity, fields, filters, order_by, limit)从一个实体读取行
get_metric(metric, dimensions, filters, limit)计算预先批准的聚合值
semantic_search(entity, query, k, filters)在实体的嵌入列上使用 pgvector 进行最近邻搜索
audit_verify()验证防篡改审计链

筛选条件采用 {field, op, value} 格式;运算符为 =, !=, <, <=, >, >=, like, in, not in, is null, is not null。值始终作为绑定参数传入,绝不会内联。

将其接入 MCP 客户端

语义层 YAML 文件位于 SQL_STEWARD_LAYER 指定的位置。Claude Desktop(claude_desktop_config.json):

{
  "mcpServers": {
    "sql-steward": {
      "command": "sql-steward",
      "env": {
        "SQL_STEWARD_LAYER": "/full/path/to/semantic.yaml",
        "SQL_STEWARD_DB_URL": "postgresql+psycopg://[email protected]/warehouse"
      }
    }
  }
}

SQL_STEWARD_DB_URL 是一个 SQLAlchemy URL,因此同一个服务可以读取 SQL Server(mssql+pyodbc://...)、Postgres(postgresql+psycopg://...)或 SQLite(sqlite:///path.db)。使用 extras 安装相应的驱动:pip install "sql-steward[postgres]" 或 "[mssql]"。

可选:其余技术栈

语义层是主要的控制机制。以下是额外的层,均为可选启用;如果未安装相应的库,则不会执行任何操作:

pip install "sql-steward[rbac,mask,audit]"

export SQL_STEWARD_POLICY=/path/to/policy.yaml   # query-warden second-pass role check
export SQL_STEWARD_ROLE=analyst
export SQL_STEWARD_MASK=1                         # pii-veil masks anything left in results
export SQL_STEWARD_AUDIT_DB=logs/steward.db       # agent-blackbox audit chain (on if installed)
export SQL_STEWARD_QUERY_BUDGET=200               # persistent per-role query cap (SQLite-backed)
export SQL_STEWARD_BUDGET_WINDOW=3600             # optional: make the cap a sliding window, in seconds
export SQL_STEWARD_EMBED_URL=http://localhost:11434/api/embeddings  # local embeddings for semantic_search
export SQL_STEWARD_EMBED_MODEL=nomic-embed-text

语义搜索(pgvector)

为实体添加一个指向 pgvector 列的 search 区块,Agent 就会获得一个 semantic_search 工具,其治理方式与其他工具完全相同(拒绝访问 PII、对结果进行脱敏、审计调用):

entities:
  documents:
    table: documents
    fields:
      id: {type: int}
      title: {type: text}
      embedding: {type: vector}
    search:
      vector_column: embedding
      dim: 768
      returns: [id, title]

查询文本在本地生成嵌入(将 SQL_STEWARD_EMBED_URL 设置为本地 Ollama 端点,这样数据就不会离开内部环境),并使用 pgvector 的 <=> 运算符进行匹配。仅支持 PostgreSQL。嵌入列绝不会返回。

  • query-warden 会根据角色策略重新检查编译后的 SQL。
  • pii-veil 会对结果行中残留的任何 PII 进行脱敏。
  • agent-blackbox 会将每次调用记录到哈希链式账本中;sql-steward audit-verify 会检查该账本。

安全模型

sql-steward 管控模型到数据库的路径。它假定调用其工具的进程是可信的。这条边界决定了安全部署还是暴露风险,因此值得明确说明。

无论模型提出什么要求,它都会执行的防护。 每次调用都会经过同一套关卡:从设计上只读、检索前拒绝访问受阻的 PII、可选的 query-warden 角色检查、由 pii-veil 对结果进行掩码处理,并对调用进行哈希链审计。即使模型被越狱,也无法写入、读取受阻列,或访问该层未定义的实体。这些防护是由设计保证的,而不是依赖模型乖乖听话。

它不会做什么。 sql-steward 是执行层,而不是入口。

  • 它不会对调用方进行身份验证。SQL_STEWARD_ROLE 是配置项,不是经过验证的身份;sql-steward 会信任传入的角色是正确的。
  • 它不会解析每位用户的权限。如果不同用户应当看到不同数据,调用方必须将用户映射到角色,并在调用前设置该角色。角色检查负责执行策略,而不负责判定你的身份。
  • 它不会保护传输安全。通过 stdio 在本地主机上运行,或将它置于能够终止 TLS 的组件之后。
  • 它不会管理密钥。它从环境中读取 SQL_STEWARD_DB_URL 和审计密钥 AGENT_BLACKBOX_KEY;请从密钥存储中提供这些值,而不要将其写入受版本控制的文件。

你需要提供什么。 sql-steward 专为部署在可信调用方之后而设计,无论调用方是同一台机器上的 MCP 主机,还是已经完成用户身份验证的网关。

  • 从可信进程调用它:由你控制的 MCP 主机通过 stdio 启动的子进程,或由网关后的服务;该网关会验证请求,并在调用前将已验证的身份映射到角色。
  • 为它提供权限遵循最小权限原则的只读数据库账户。编译器无法表示写入操作,但只读授权可作为纵深防御;如果某个依赖项出错,它也能将影响范围控制在合理限度内。
  • 将数据库和任何本地嵌入端点留在专用网络中。不要将服务暴露给不可信的调用方。
  • 将连接字符串和审计密钥视为机密信息。

它针对的威胁。 该设计假定模型能力强且可能已遭入侵,而操作者可信。它会限制 Agent 对数据库的操作范围。它不能取代你的身份提供商,也不适合单独暴露在不可信网络中。从你信任的主机或网关后面连接到真实数据库,模型所能做的就只有这三项保证所允许的范围。

有何不同

典型的 SQL MCP 会验证模型编写的任意 SQL(黑名单:拦截不安全的内容)。sql-steward 则根据你编写的定义编译 SQL(允许列表:只允许访问定义中描述的内容)。只读和 PII 保证由设计本身实现,而非通过检查来保证;并且三个引擎上的查询接口保持一致。

与语义层对比(Cube、dbt Semantic Layer、Cortex Analyst、Genie)

最接近的工具不是其他 MCP 服务,而是语义层。Cube 和 dbt Semantic Layer 已经提供编译后的纯指标访问能力,而 Snowflake Cortex Analyst 和 Databricks Genie 都可以基于受治理的模型回答自然语言问题。如果你使用它们的平台,就用它们。本项目适用于它们无法覆盖的场景:

  • 可在本地部署,并可与外部网络隔离。 sql-steward 是一个 pip 安装包,可连接 SQL Server、Postgres 或 SQLite,无需账户或数据仓库,也不会让数据离开所在机构。Cortex Analyst 属于 Snowflake,Genie 属于 Databricks,而 Cube 的云端功能则默认依赖其服务。对于不能向供应商发送数据的工厂或医院来说,这一区别就是决定性因素。
  • 在检索前、同一关卡处拒绝 PII。 被屏蔽的字段会在编译时对所有调用方拒绝,因此模型无法读取策略禁止的内容,甚至不能据此进行推理。语义层会管理你可以查询哪些指标,但它们并非旨在保证带标签的列永远不会到达模型。
  • 为整个 Agent 提供一份防篡改审计记录,而不只是 SQL。 在组合式技术栈中,同一关卡模式还会用于 KQL、文档检索和 Agent 记忆,并由同一份哈希链式账本记录。数据仓库语义层管理数据仓库,但不会管理 Agent 的其他工具。

简而言之:语义层让平台上的查询安全;sql-steward 则让 Agent 在你的基础设施上安全运行,并覆盖它能访问的所有界面。

这并不意味着要与世隔绝。整个行业正逐渐采用 Apache Ossie(Open Semantic Interchange 格式,目前由 ASF 孵化,源自 Snowflake、dbt 和 Salesforce 工作组),将其作为在工具间迁移语义模型的中立方式,而 sql-steward 支持该格式:

sql-steward export semantic.yaml --out model.osi.yaml

实体、联接和指标分别映射到 OSI 数据集、关系和指标。 OSI 目前还没有治理方面的词汇,因此让语义层成为契约而非目录的部分,包括 PII 标签、策略屏蔽、指标允许列表和检查项,都会放在 custom_extensions 下,使用 vendor_name: SQL_STEWARD;无法表示的内容会作为说明报告到 stderr,而不是被悄然丢弃。输出可通过 Ossie 自带的验证器,因此为 sql-steward 编写的语义层可以交给任何能够读取该标准的工具使用。

扩展到实际规模的架构

语义层刻意采用手动编写,以便像代码一样阅读和审查。这对于几十张表来说是恰当的默认方式,对于几千张表来说则不合适:没人会为包含 10,000 张表的 ERP 手写语义层,而且在一次 list_entities 调用中返回整个语义层也不适合 Agent 的上下文。扩展方式如下:

  • 先生成初稿,再进行审查。 sql-steward init --from-db <url> 会读取实时架构、映射列类型、根据列名启发式规则建议 PII 标签(倾向于过度标记,让泄漏问题成为需要审查的修改,而非默认结果),并从外键推断联接关系。它会生成一个可直接加载和验证的语义层草稿,并在文件头说明需要收窄的内容。目的不是自动暴露所有内容,而是消除从空白开始的问题,让大型架构从一份可审查的文件开始,而不是手动逐项输入。

    sql-steward init --from-db "postgresql+psycopg://readonly@db/warehouse" --out semantic.yaml
    # then delete entities you do not need, check the PII tags, add metrics
    
  • 范围比规模更重要。 受治理的语义层应该只暴露 Agent 实际需要的少数实体,而不是整个数据库。即使架构包含 10,000 张表,也仍可将契约限定为 20 个实体;关键在于决定哪些内容应纳入(使用 --include/--exclude 仅为部分内容生成草稿),这是模型的特性,而非工具的限制。

  • 发现能力随语义层增长。 随着语义层扩展,可浏览 list_entities/describe_entity;在面向一个非常庞大的单一语义层之前,接下来需要完善的是对这些响应进行搜索和分页。

开发

git clone https://github.com/Pawansingh3889/sql-steward
cd sql-steward
pip install -e ".[dev]"
pytest -q

许可证

MIT

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。