AI智能体在理解数据库之前就获得了数据库访问权限。
这个顺序是错误的。
真实的生成数据库很少是不言自明的。重要的表并不总是命名为 orders。客户表可能被称为 t_bd_customer。一个字段可能带有业务关键状态码,只有了解其背后的系统才能理解其含义。数据仓库可能将原始操作数据、清洗后的维度和聚合事实拆分到名为 ods、dw 和 staging 的模式中。模式在技术上是可见的,但其含义并非如此。
所以我构建了 db-semantic-mcp:一个小型 MCP 服务器,为 AI 编码智能体提供数据库的安全语义地图。
它提供表名、列类型、注释、示例行,以及基于 LLM 的模式搜索。它支持 PostgreSQL 和 SQL Server。它与兼容 MCP 的智能体客户端(如 OpenCode、Claude Code、Cursor 及类似工具)配合使用。
它刻意不执行 SQL。
这个边界正是关键所在。
大多数面向智能体的数据库集成从查询执行开始。给模型一个连接字符串,添加一个 SQL 工具,可能再加一个只读角色,然后让它向数据库提问。
这可能很有用。这也是重要的一步。
在智能体编写或运行查询之前,它需要回答更基本的问题:
这些不是 SQL 执行问题,而是数据库理解问题。
db-semantic-mcp 专注于这一层。它给智能体足够的结构来导航数据库,而不会把数据库变成一个远程操控界面。
该服务器提供四个 MCP 工具:
| 工具 | 用途 |
|---|---|
list_tables |
列出包含模式名和表注释的数据库表。 |
describe_table |
检查列、类型、可空性和列注释。 |
sample_data |
从表中获取少量示例行。 |
search_schema |
使用兼容 OpenAI 的 LLM 对表和列进行语义搜索。 |
前三个工具是直接的元数据和采样操作。它们让智能体像开发者一样检查数据库:列出表、打开一张表、查看列、检查几行数据。
第四个工具才是语义层的关键所在。
search_schema 将缓存的模式快照与一个可选的 Markdown 文件结合起来,该文件描述了你的业务术语、命名约定和数据库设计决策。然后模型可以解析自然语言请求,例如:
customer receivables
WIP inventory
sales order
应收账款
进入可能重要的表和列中。
这对于那些表名在技术上保持一致但对代理来说不明显的数据库尤其有用。ERP数据库、遗留SQL Server系统以及大型仓库模式通常属于这一类。
没有需要学习的新本体格式。没有需要部署的向量数据库。没有单独的目录服务。
你只编写一个Markdown文件。
例如:
# Database Semantic Context
## Naming Conventions
- `ods.*` tables contain raw operational data.
- `dw.*` tables contain modeled fact and dimension tables.
- `staging.*` tables are temporary ETL staging tables.
## Business Terms
| Business term | Table(s) |
| --- | --- |
| Customer | ods.bd_customer, dw.dim_customer |
| Inventory | dw.fact_inventory_snapshot |
| WIP / work in progress | dw.fact_wip_by_lot |
## Design Decisions
- Monetary amounts are stored in integer cents.
- `_modified_at` columns are incremental sync watermarks.
- Soft deletes use `doc_status = 'D'`.
该文件会被加载到模式搜索提示中。它是数据库物理结构与开发人员或业务用户实际使用的术语之间的桥梁。
重要的设计选择是让语义层贴近团队。它可以位于项目旁边。它可以像文档一样被审查。它可以被更改,而无需重新索引向量存储或迁移元数据系统。
因为智能体需要的第一个安全原语并不总是查询工具。
如果一个智能体可以执行任意 SQL,即使是只读 SQL,安全问题也会立刻变得更大。你需要考虑权限、行级访问、查询成本、数据泄露、审计日志以及通过数据进行的提示注入。这些问题是可以解决的,但并非没有代价。
db-semantic-mcp 采取了更聚焦的立场:首先让智能体看到结构和含义。
这使得该工具在更保守的环境中也能发挥作用。一个团队可能早就愿意向智能体暴露表元数据、注释和几行示例数据,但不愿意给智能体提供通用的 SQL 执行接口。服务器仍然连接数据库,因此应谨慎配置,但其产品边界有意做得更小。
结果并不是一个 text-to-SQL 平台。它是 text-to-SQL 之前的层。它帮助智能体理解自己所处的位置。
第一个实现支持 PostgreSQL。当前版本也通过相同的 MCP 接口支持 SQL Server。
后端根据 DATABASE_URL 方案选择:
postgresql://user:pass@localhost:5432/mydb
sqlserver://user:pass@host:1433?database=mydb&encrypt=disable
这一点很重要,因为大量有价值的业务数据并不整齐地存放在 Postgres 应用数据库中。它们存在于 SQL Server 中,存在于 ERP 系统中,存在于那些拥有数千张表、注释不一致、历史命名约定以及只有公司内部少数人才能理解的架构的数据库中。
对于这些数据库,db-semantic-mcp 包含了诸如 schema 过滤器和表前缀过滤器之类的缓存控制。如果 SQL Server 数据库包含数千张表,但有价值的业务表共享类似 t_pur_、t_sal_、t_stk_ 或 t_bd_ 的前缀,那么 schema 缓存就可以聚焦于这些区域。
这并不是为了让一个玩具数据库更容易查询,而是为了让混乱的真实数据库能够在无需假装它们很整洁的情况下,被智能体(agent)所导航。
一旦在 MCP 客户端中注册,工作流程就很简单。
智能体可以从一个宽泛的范围开始:
list_tables schema=dw
然后检查候选表:
describe_table table=dw.fact_inventory_snapshot
然后查看几行:
sample_data table=dw.fact_inventory_snapshot limit=3
或者进行语义搜索:
search_schema keyword="customer receivables"
search_schema keyword="应收账款"
代理无需凭记忆猜测表名。它不需要用户在每个提示中粘贴模式转储。它可以向数据库元数据服务器请求相关上下文,然后在编码任务中使用该上下文。
例如,如果任务是修改ETL管道、添加报告端点或调试数据映射问题,代理可以先发现数据库结构,而不是凭空想象。
这就是价值:行动之前更好的 grounding。
服务器通过环境变量进行配置:
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
SEMANTIC_FILE=/path/to/SCHEMA.md
LLM_BASE_URL=https://api.openai.com/v1
LLM_API_KEY=sk-...
LLM_MODEL=gpt-4o-mini
仅在进行语义搜索时需要 LLM_API_KEY。元数据工具在没有它的情况下也能正常工作。
MCP 客户端可以将其注册为本地服务器:
{
"mcp": {
"db-semantic": {
"type": "local",
"command": "pg-semantic-mcp",
"environment": {
"DATABASE_URL": "postgresql://user:pass@host:5432/dbname",
"SEMANTIC_FILE": "/path/to/SCHEMA.md",
"LLM_API_KEY": "sk-..."
}
}
}
}
命令名称仍然使用 pg-semantic-mcp,以兼容原有的仅支持 PostgreSQL 的版本。由于服务器现在支持多种后端,包和仓库已改用更宽泛的名称 db-semantic-mcp。
当代理需要数据库上下文,但不应该一开始就执行 SQL 时,db-semantic-mcp 非常有用。
适合的场景包括:
它并不试图取代 BI 平台、数仓目录、治理产品或完整的文本到 SQL 系统。
这是一个被忽视的小小基础能力:让代理在对数据库采取行动之前先理解数据库。
我认为代理工具将分为两类。
一些工具会让代理更强大。它们将允许代理执行、修改、部署、管理和自动化更多系统操作。
其他工具会让代理更加脚踏实地。它们将以减少猜测的方式暴露状态、约束、就绪情况、历史、元数据和语义。
db-semantic-mcp 属于第二类。
它不会让代理无所不能。它给代理一张地图。在真实的工程工作中,这通常是更安全、更有用的第一步。
——
一个热爱技术的程序员,喜欢分享前沿AI知识和开发经验。