【金仓数据库征文】给金仓装上“嘴替“:用 MCP Server 让 AI Agent 说人话查数据库

我给国产数据库接上了AI,然后亲手攻击了它五次

事情是这样的。

做数据库这行的朋友,大概都接过这种活。业务方隔三差五在群里丢来一句,上周谁买得最多?

就这么一句话,DBA就得放下手里的活,给他写一段SQL。

我一直觉得这事特别拧巴。这类临时取数,占用的是团队里最贵的人力,产出的却是最标准化的东西。问题用中文描述,答案就躺在数据库里,中间缺的,其实就是一个翻译。

这个翻译的活,以前只能人来干。

直到2025年下半年,一个叫MCP的协议,把这件事的解法给定了型。

MCP,Model Context Protocol,Anthropic提出来的,现在OpenAI、Google这些主流玩家全都跟进了。你可以把它理解成AI应用和外部工具之间的USB-C接口。你把数据库的能力包装成一个MCP Server,任何兼容MCP的AI客户端,Cline也好,各家自研的Agent也好,插上来就能用。大模型自己决定该查哪张表、该写什么SQL,查完再用大白话把结果讲给你听。

道理听着都顺。但你如果关注这个领域的话,会发现网上聊MCP概念的文章一大堆,真正把它落到一个国产数据库上、从头到尾跑通给你看的,不多。

所以我干脆自己来。手边正好有台鲲鹏服务器,上面跑着KingbaseES,我就亲手给它写了一个MCP Server,把中文问数这条链路,完完整整跑了一遍。

先把结论放这,链路通了,而且通得很顺。但我跟你说,这篇文章真正想聊的不是怎么跑通,跑通是最容易的部分。

真正花心思的地方,是安全。

这个先按下不表,后面细说。

先说结构。

整条链路只有三个角色,AI客户端,MCP Server,金仓数据库。中间那个MCP Server是我写的,满打满算100行Python。怎么说呢,它就是个翻译官,把大模型的意图,翻译成数据库听得懂的话。

它向AI暴露四个工具。

list_tables,列出库里有哪些业务表、各有多少行,让Agent先看清战场再动手。describe_table,看某张表的结构,顺带给3行示例数据,Agent知道了字段名和类型,SQL才写得对。run_query,执行一条只读的SELECT,这是核心能力,也是安全防线所在。最后一个db_info,返回数据库版本和连接身份,用来验明后端的真身。

驱动用的是金仓官方推荐的PG生态驱动psycopg2,连54321端口。Server本身用Anthropic官方mcp SDK里的FastMCP,一个装饰器,就能把普通Python函数变成MCP工具,大概长这样。

frommcp.server.fastmcpimportFastMCP mcp=FastMCP("kingbase")@mcp.tool()defrun_query(sql:str)->str:"""执行一条只读 SELECT 查询并返回结果(最多 50 行)。"""...# 三道安全闸 + psycopg2 执行,后面细说

写的过程里有个细节,我觉得特别有意思。

工具函数的docstring,就是上面那行「执行一条只读SELECT查询」,它不是注释,是给大模型看的说明书。Agent就是靠读这段话,来判断什么时候该调哪个工具。

也就是说,你写的注释,第一次有了一个AI读者。

这是写MCP Server和写普通后端接口最不一样的地方。接口文档以前是写给人看的,人看不懂,大不了来问你。AI看不懂,它就直接用错。

好,Server写完了。但光我自己说它是MCP Server,不算数,得拿协议说话。

我用官方mcp SDK写了一个真正的MCP协议客户端去连它,走完整的initialize、tools/list、tools/call握手流程。

截图里的每一行,都是真实的协议往返。

握手成功,协议版本2025-11-25,Server名kingbase。这说明它是标准MCP,不是我自己发明的野接口。

客户端自动拉到了4个工具和它们的说明,这就是Agent「知道自己能干什么」的来源。

db_info吐出来一行东西,KingbaseES V009R003C018 | ai_ro | test。后端确实是金仓,而且注意连接身份,是一个叫ai_ro的只读账号。

这个ai_ro,你先记住它,待会它是主角。

然后run_query跑了一条GROUP BY聚合,真实返回了消费额排名。最后我故意喂了它一条DELETE,服务端直接回了一句,已拒绝,只允许SELECT/WITH查询。

到这一步,事情就有意思了。任何MCP兼容的AI应用,都能用完全相同的协议,接进这台金仓Server。我用SDK客户端能连上,Cline自然也能连上。

接下来,就是最好玩的部分了。

库里是一套电商味的测试数据,一张member会员表,一张orders订单表。我给Agent出了三个纯中文的问题,让它自己翻译成SQL、经MCP查金仓、再用中文把答案讲出来。

第一问,谁是消费冠军,一共花了多少钱?

Agent生成了一条单表聚合,select member_name, sum(amount) s from orders group by member_name order by s desc limit 1,然后回答,消费冠军是赵敏,累计消费10556.00元。

规规矩矩,热身题。

第二问我加了点难度,7月1号以来销售额最高的三样商品是什么?

这次它得自己想明白时间过滤怎么写。回答,7月以来销售额Top3,游戏显卡5999元,NAS存储4599元,4K显示器3299元。

第三问才是真正的考验,积分最高的会员是谁?他买过东西吗?

这题阴就阴在,积分在member表,订单在orders表,它必须自己意识到,这得做两表JOIN。结果Agent老老实实写出了member m left join orders o on o.member_name = m.name这样的连接,然后回答,积分最高的是赵敏,25999分,他有4笔订单。

三个问题层层递进,单表聚合、时间过滤、两表JOIN,它都正确地组织了SQL。而且我想强调一句,答案里的每一个数字,10556.00也好,25999也好,4笔也好,全部来自金仓的真实返回,没有一处硬编码。

看到这你可能觉得,这不就成了吗,接上生产库开用啊。

先别急。

我知道很多DBA朋友读到这里,心里其实已经开始发毛了。让一个大模型直连我管的库?它今天能写SELECT,明天会不会给我来一句DROP?

我特别理解这种警惕。坦率的讲,如果有人要往我负责的生产库上接一个AI,我的第一反应也是拒绝。

而且很多人以为,这套东西的难点是让AI把SQL写对。真不是,写对SQL是现在这批大模型的强项,难的是让它写不了不该写的东西。一个能连生产库、还听大模型指挥的服务,如果只做到「能查」,那不是功能。

是事故。

所以这套东西里我真正花心思的部分,不是问数演示,是三道互相独立的安全闸门,任何一道,都能单独兜底。

第一道闸,数据库账号本身只读。

还记得让你记住的那个ai_ro吗,MCP Server连库用的就是它,这个账号只被GRANT SELECT。截图第一行是我做的一个实验,绕过所有代码,直接拿ai_ro连上数据库执行DELETE,金仓在权限层就把它顶了回来,ERROR: permission denied for table orders

这是最硬的一道。它的意义在于,就算我代码里的防线全被绕过,数据库自己,也不会让AI写入一个字节。

第二道闸,会话级只读。代码里conn.set_session(readonly=True),再加一句SET statement_timeout='5s',防止AI写出慢查询把库拖垮。

第三道闸,应用层SQL白名单。run_query只放行单条SELECT或WITH开头的语句,并且用正则扫描写操作关键字。

这道闸严不严,得打过才知道。我让Agent对着它,发起了五种攻击。

先来最直白的,delete from orders。拒绝,只允许SELECT/WITH查询。

升级一点,drop table member。拒绝,理由同上。

换个思路,多语句注入,select 1; drop table member,前半句人畜无害,后半句图穷匕见。拒绝,只允许单条语句。

再阴一点,大小写绕过,UpDaTe这种写法,赌你的正则只认小写。想多了,照样拒绝。

最后一招是我自己都觉得有点损的,子查询藏写,select (delete ... returning 1),把DELETE藏进一个SELECT的壳里。还是被拒,检测到写操作关键字。

五连败。

而作为对照组,一条正常的select count(*),顺利放行,返回20。该拦的全拦住,该放的一条没误伤。

写这三道闸的时候,我脑子里总飘着一个两千多年前的工程,都江堰。

李冰治水,没有修一道无坚不摧的大坝去硬堵,而是修了鱼嘴、飞沙堰、宝瓶口三道结构,各管一段,互相独立,哪一道出了状况,剩下的照样能把水安顿好。真正让人放心的系统,从来不是靠一堵完美的墙,是靠几道互不依赖的防线,一层一层把风险卸掉。

数据库安全这块,我觉得是一个道理。正则可能有疏漏,会话设置可能被重置,但只要ai_ro这个账号在权限层是只读的,天塌下来,数据还在。

安全之外,还有一件事,我觉得从第一版就得立住。

留痕。

生产环境绕不开一个问题,出了事,能不能查清是谁、在什么时候、让AI对数据库做了什么。所以我给每个工具都加了审计日志。

一次问数会话加越权尝试跑完,calls.log里完整记下了每一次调用,三条正常的业务查询SQL,以及两条被拒的drop table和delete。

注意,被拦截的攻击,也照样留痕。

这正是安全审计最想要的东西,不仅记成功,更要记下「有人试图越权」这件事本身。

说实话,这个日志模块就是个40行的毛坯,生产上肯定还要接统一日志平台、加调用方身份、做异常告警。但「每一次AI触达数据库都可回溯」这个原则,从第一版就得立住,后面才有得谈。

最后说说怎么用起来。

这可能是整套东西里最让我舒服的部分,在真实工位上接入它,不需要写一行胶水代码。任何MCP客户端,配置里加一段就行。

{"mcpServers":{"kingbase":{"command":"ssh","args":["root@<服务器>","python3","/root/mcp/kingbase_mcp.py"]}}}

重启客户端,kingbase就出现在工具列表里了。之后你对着聊天框问一句,这个月各产品卖了多少,大模型会自动调list_tables摸清有哪些表,调describe_table看清字段,再调run_query执行查询,最后把结果讲给你听。

MCP的价值就在这,Server写一次,所有AI客户端通用。

聊到这,这趟折腾算是可以收尾了。

100行Python,我给KingbaseES装上了一个中文入口。业务方说人话,大模型经MCP把它翻译成SQL去查金仓,再用人话把答案讲回来。

但如果这篇文章只能留下一句话,我希望是这句。

让AI连上生产数据库,从来不是技术上能不能做到的问题,而是敢不敢让它做,以及出了事,能不能兜得住的问题。

敢,不是胆子大,是每一道防线都亲手打过、确认它接得住之后的踏实。金仓在权限层顶回DELETE的那一下,就是这套方案敢称「可上生产」的地基。

MCP把AI和数据库的连接方式标准化了。而从这一路跑下来的结果看,金仓,完全站得进这个新生态。

回到开头那句「上周谁买得最多」。

下次业务方再丢来这么一句话,DBA可以不用放下手里的活了。让AI去查,让闸门守着底线,让日志记下一切。

这篇里所有的判断,没有一条是从别人文章里抄来的,都是在那台鲲鹏服务器上,一条命令一条命令敲出来的。

亲自去做,然后才敢下判断。

我觉得,这就是笃行的意思。

谢谢你看我的文章,我们,下次再见。

/ 作者:笃行其道