1. 为什么要把 ERP 数据接给 AI 助手很多做电商或制造业的朋友都遇到过这个场景ERP 系统里躺着商品、订单、成本、库存这些核心数据但 AI 助手完全碰不到。你问它上个月利润最高的 10 个 SKU 是哪些它只能干瞪眼因为它既不会连 SQL Server你也不该直接把数据库账号密码丢给它。MCPModel Context Protocol模型上下文协议就是解决这个断层的东西。它让你把查数据库这件事封装成一个受控的工具ToolAI 助手负责决定什么时候调用、传什么参数你的服务端负责真正执行 SQL 并返回结果。边界清晰权限可控。这篇我会用一个电商 ERP 场景从零用 Node/TS 写一个 MCP 服务器把 SQL Server 里的商品表、订单表、成本利润表暴露成 AI 能调用的工具再通过 TaoToken 统一 Key 接入模型通道。全程可复制跟着敲就能跑起来。适合谁看有 Node/TS 基础、手上有 SQL Server 数据、想让 Claude 或 Codex 直接查业务库的后端同学。不需要你懂 MCP 协议细节我会把关键机制讲清楚。2. TaoToken 前置准备统一 Key 与接入通道在写服务端之前先把模型通道准备好。MCP 服务器本身只负责提供工具真正调用工具的是 AI 客户端Claude Desktop、Codex CLI 等。这些客户端要连大模型就需要一个稳定的 API 通道。TaoToken 在这里的角色是统一 Key 和 API 通道你注册后拿到一个 Key就能在多个模型客户端里复用不用每个客户端单独配一套。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。具体操作分三步第一步登录控制台创建 API Key。地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 在 API Keys 页面点新建复制生成的 Key形如 sk-xxxx这个 Key 只显示一次存好。第二步如果你要长期跑编码或 Agent 任务建议看下 Coding Plan地址 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它针对高频调用做了额度优化比按次计费划算。第三步验证模型通道是否通。可以直接在模型对话页测试地址 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 选一个模型发一句话能正常回复说明 Key 和通道都没问题。注意MCP 服务器和模型通道是两件事。MCP 服务器跑在你本地或内网负责连 SQL Server模型通道负责 AI 的推理。两者通过 AI 客户端串起来别搞混。3. 从零搭 MCP 服务器环境、依赖与 SQL Server 连接3.1 环境准备你需要 Node.js 18 以上建议 22一台能连通的 SQL Server示例用your-server-ip,1433库名demo_db以及一个支持 MCP 的客户端。下面所有库名、表名、IP 都是示意替换成你自己的。3.2 项目初始化mkdir erp-mcp cd erp-mcp npm init -y npm install modelcontextprotocol/sdk mssql zod npm install -D tsx typescript types/node types/mssqltsx让我们直接跑 TypeScript省掉编译步骤调试期很省事。3.3 连接 SQL Server用mssql底层是 tedious 驱动封装一个带连接池的配置。新建src/db.tsimport sql from mssql; export const sqlConfig: sql.config { user: process.env.ERP_SA || sa, password: process.env.ERP_PWD || your_strong_password, server: your-server-ip, port: 1433, database: demo_db, options: { encrypt: false, trustServerCertificate: true, }, pool: { max: 10, min: 0, idleTimeoutMillis: 30000, }, }; export async function getPool() { return sql.connect(sqlConfig); }密码走环境变量ERP_PWD别硬编码进仓库。生产环境务必给 MCP 单独建一个只读账号只授予SELECT权限。3.4 定义三个业务工具新建src/index.ts注册三个工具查商品、查订单、按 SKU 汇总成本利润。import { McpServer } from modelcontextprotocol/sdk/server/mcp.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; import { z } from zod; import { getPool } from ./db.js; import sql from mssql; const server new McpServer({ name: erp-mcp, version: 1.0.0 }); // 工具1按关键词/分类查商品 server.tool( query_products, 按关键词或分类查询 ERP 商品表(tb_products), { keyword: z.string().optional().describe(商品名模糊匹配), category: z.string().optional().describe(商品分类), }, async ({ keyword, category }) { const pool await getPool(); const req pool.request(); let sqlText SELECT TOP 50 ProductId, ProductName, Sku, Price, Category FROM tb_products WHERE 11; if (keyword) { req.input(kw, sql.NVarChar, %${keyword}%); sqlText AND ProductName LIKE kw; } if (category) { req.input(cat, sql.NVarChar, category); sqlText AND Category cat; } const r await req.query(sqlText); return { content: [{ type: text, text: JSON.stringify(r.recordset, null, 2) }], }; } ); // 工具2查指定日期范围的订单 server.tool( query_orders, 查询指定日期范围内的订单(tb_orders), { start: z.string().describe(起始日期 YYYY-MM-DD), end: z.string().describe(结束日期 YYYY-MM-DD), }, async ({ start, end }) { const pool await getPool(); const req pool.request(); req.input(s, sql.Date, start); req.input(e, sql.Date, end); const r await req.query( SELECT OrderId, ProductId, Qty, Amount, Status FROM tb_orders WHERE CreateAt BETWEEN s AND e ); return { content: [{ type: text, text: JSON.stringify(r.recordset, null, 2) }], }; } ); // 工具3按 SKU 汇总某月成本与利润 server.tool( profit_summary, 按 SKU 汇总某月成本与利润, { month: z.string().describe(月份 YYYY-MM), }, async ({ month }) { const pool await getPool(); const req pool.request(); req.input(m, sql.NVarChar, ${month}%); const r await req.query( SELECT ProductId, SUM(Amount) AS Revenue, SUM(Cost) AS Cost, SUM(Amount - Cost) AS Profit FROM tb_orders WHERE CreateAt LIKE m GROUP BY ProductId ORDER BY Profit DESC ); return { content: [{ type: text, text: JSON.stringify(r.recordset, null, 2) }], }; } ); const transport new StdioServerTransport(); await server.connect(transport); console.error(ERP MCP server running on stdio);三个工具都用req.input()参数化这是防注入的关键后面排障会细说。3.5 启动与注册到客户端本地跑起来export ERP_PWD你的密码 npx tsx src/index.ts看到ERP MCP server running on stdio就说明服务端起来了。接着注册到 Claude Desktop 的claude_desktop_config.json{ mcpServers: { erp: { command: npx, args: [tsx, /path/to/erp-mcp/src/index.ts], env: { ERP_PWD: 你的密码 } } } }重启客户端后AI 就能看到这三个工具了。4. 验证请求让 AI 真正查一次 ERP 数据配置完成后直接在对话框里问帮我查利润最高的 5 个 SKU2026-06。AI 会自动调用profit_summary工具传入month: 2026-06服务端执行 SQL 返回 JSONAI 再把结果整理成人话给你。你也可以手动验证工具是否注册成功。在支持 MCP 的客户端里通常有工具列表能看到query_products、query_orders、profit_summary三个条目。如果客户端支持直接调用传一组参数试试{ keyword: 手机, category: 数码 }返回的recordset就是tb_products里匹配的商品。这一步通了说明从 SQL Server 到 AI 助手的链路完整了。如果你用的是 Codex CLI 或 Cursor注册方式类似都是把 MCP 服务器命令和参数写进各自的配置文件。模型通道那边把 TaoToken 的 API Key 填进客户端的模型配置即可接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 有详细说明。5. 本篇常见错排查5.1 SQL 注入千万别拼字符串我试过图省事写WHERE ProductName LIKE %${keyword}%一旦有人让 AI 传入 OR 11全表就裸了。正确做法是req.input()参数化让 mssql 走预编译参数。上面三个工具全部用了参数化这是底线。5.2 连接池与超时配置pool上限避免高并发把 SQL Server 连接打满。工具里用await getPool()复用连接别每次sql.connect新建。客户端超时会掐进程长查询加SET LOCK_TIMEOUT或限制TOP N。5.3 工具描述写清楚AI 靠工具描述决定调不调、怎么调。query_orders的描述里写明日期格式 YYYY-MM-DDAI 就不会传错格式。描述含糊AI 就容易瞎调。5.4 权限最小化生产环境给 MCP 单独建只读账号只授SELECT。别用 sa 账号跑一旦工具被滥用损失可控。5.5 客户端连不上服务端先确认npx tsx src/index.ts能独立跑起来再看客户端配置里的路径是不是绝对路径。相对路径在客户端启动时容易找不到文件。6. 下一步把更多数据源接进来MCP 把数据库能力变成 AI 可调用、你可控边界的工具是今年很值得上手的技术。这篇用 ERP SQL Server 走完了全流程建服务端、连库、定义工具、防注入、注册客户端。下一步你可以加 Resources把表结构暴露给 AI 做 schema 理解或者加 Prompts固化月度利润复盘这类话术。想验证模型通道是否稳定可以去模型对话页多试几个模型长期跑 Agent 任务的话Coding Plan 的额度更合适。接入过程中遇到工具注册或 Key 配置的问题优先查接入文档大部分坑那里都有答案。