“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 改成 ES2022modulemoduleResolution 改成 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);

这段代码看似简单,但有两个坑必须防住:

  1. readonly: true:这是保命的配置。MCP Server 是 AI 在你本地的“手”。AI 一旦脑子抽风发来一条 DELETE FROM users,你如果不锁只读,本地引擎会二话不说直接执行。
  2. 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.
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐