30分钟,用 MCP 给 AI 开个数据库后门
“Any sufficiently advanced technology is indistinguishable from magic.” — Arthur C. Clarke
“我想在 Claude 里直接查线上用户数据,能搞不?”
上周五下班前产品经理提了这个要求。以前碰到这种跟内网系统打交道的活,我得老老实实写一套 REST API、搞鉴权、配前台页面,折腾下来最少也得三四天。
这次我试了下最近很火的 MCP(Model Context Protocol)。
结果:半小时写完,当场上线。
Claude 顺利查到了本地数据库。整个过程比预想的简单得多。
为什么这玩意儿不需要写 API
之前让 AI 调接口,链路挺烦的:AI 输出参数 -> 你的客户端接住 -> 发 HTTP 请求 -> 后端处理 -> 原路返回。
MCP 走的是另一条路。
它其实就是个标准总线。你在本地写个 Server,通过标准输入输出(stdio)跟客户端(比如 Claude Desktop)通讯。AI 启动时会通过 JSON-RPC 问你的 Server“你能干啥”,Server 把工具列表一扔,AI 就可以直接下发参数调用了。
没有网络通信,不用配 Web 框架。你写的代码,就是给 AI 用的一个“硬件驱动”。
极简环境:两步搭完
直接在干净目录下跑初始化和装依赖,别搞那些多余的脚手架:
# 装官方SDK和 SQLite 驱动
npm init -y && npm install @modelcontextprotocol/sdk zod better-sqlite3
npm install -D typescript @types/node @types/better-sqlite3 && npx tsc --init
(tsconfig.json 里把 target 改成 ES2022,module 和 moduleResolution 改成 Node16 就行,多余的配置不用动)。
核心实现:锁死 readonly
整个 Server 的核心逻辑加起来不到60行代码:
// src/index.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
import Database from 'better-sqlite3';
// 🚨 必须只读。AI 有你给的一切权限,不锁只读,后果自负
const db = new Database('users.db', { readonly: true });
const server = new McpServer({
name: 'db-query-server',
version: '1.0.0',
});
// 查用户工具
server.tool(
'query_users',
'根据条件查询用户列表,支持按 plan 筛选和 limit 限制',
{
plan: z.enum(['free', 'pro', 'enterprise']).optional().describe('套餐类型'),
limit: z.number().min(1).max(100).default(10).describe('返回上限'),
},
async ({ plan, limit }) => {
let sql = 'SELECT id, name, email, plan FROM users';
const params: unknown[] = [];
if (plan) {
sql += ' WHERE plan = ?';
params.push(plan);
}
sql += ' LIMIT ?';
params.push(limit);
const rows = db.prepare(sql).all(...params);
return {
content: [{ type: 'text', text: JSON.stringify(rows, null, 2) }],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
这段代码看似简单,但有两个坑必须防住:
readonly: true:这是保命的配置。MCP Server 是 AI 在你本地的“手”。AI 一旦脑子抽风发来一条DELETE FROM users,你如果不锁只读,本地引擎会二话不说直接执行。z.number().describe('返回上限'):这里的describe不是用来写注释的。AI 读不懂你的核心代码,它是完全靠 Zod 里的描述文字来判断这个参数该传什么的。描述写得越清晰,AI 调用时传的参数就越准。
stdio的死穴:绝对路径与工作目录
编译完(tsc)后,需要改下 Claude Desktop 的配置。mac 上的配置文件在:~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"db-query": {
"command": "node",
"args": ["/Users/你的用户名/mcp-db-server/dist/index.js"]
}
}
}
这里有两个很容易踩的深坑:
- 别用相对路径:Claude Desktop 是系统后台进程,它的启动目录跟你的项目目录完全没关系。不写绝对路径,它根本找不着你的
index.js。 - 工作目录会飘:因为 Server 是被 Claude Desktop 进程派生出来的,如果你的数据库写的是相对路径
users.db,它会被建在 Claude 自己的缓存路径下,导致你本地灌的测试数据完全查不到。代码里最好用path.resolve锁定数据库的绝对路径。
搞定收工
重启 Claude,工具栏里已经能看到 query_users 了。
我试着问了一句:查查 pro 套餐的用户有哪些?
Claude 默默调了本地的 query_users,拿到数据后回复:目前 pro 套餐的用户有张三和赵六。
产品经理在旁边看傻了,回去就把那个“用户数据查询后台”的需求单给关了。
如果只是想极速验证一些内网数据的投射,用 MCP 确实比写一套 REST API 舒服得多。
📊 数据来源与参考资料
- Model Context Protocol Spec: Anthropic MCP Docs.
- TypeScript MCP SDK Ref: Github Repository docs.
- better-sqlite3 documentation: SQLite Node driver manual.
更多推荐


所有评论(0)