各位大佬们,这篇文章AI味很浓,我是把codex当成技术交流者和实践者,再让它总结成NestJs文档。如果有介意的话轻喷。为啥要用NestJs,因为我发现codex的skill同时支持python和typescript,我又是前端开发转NodeJs开发,所以用了NestJs框架
基于 @nestjs-mcp/server 与 TypeORM 的 stdio 只读查询服务 (可以形成增删改服务,本文只有只读服务,保证数据正确及来源干净)
本文汇总实现和一次官方 MCP Client 冒烟测试为基础;后续若调整传输层、鉴权或接入方式,以最新代码和实际环境为准。
一、当前实现基线
1.1 服务形态
· 不新建独立服务或新 package:代码位于 servers 工程下的 aiSkill/ai-mcp 目录,新依赖安装于 servers 根目录。
·使用 TypeORM,并直接复用主工程已有实体,不引入 Sequelize,也不额外创建实体。
·只提供只读、语义明确的查询工具;默认只排除逻辑删除记录(is_delete=1),step_status 不限任何取值。
·以 stdio 模式运行,仅当 MCP_STDIO_ENABLED=true 时进入 MCP 分支;普通 HTTP 启动流程不受影响。
·启动脚本 start:mcp 已加入 AI_QUEUE_ENABLED=false,避免 MCP 进程额外连接 Redis/BullMQ。
1.2 对外暴露的工具
| 工具 | 作用 | 典型参数 |
|---|---|---|
| query_workflow_steps | 分页查询 ai_workflow_step,支持工作流 id、状态、类型、输出格式、模型、provider、耗时、创建时间与关键词等过滤。 | page、pageSize、stepStatus、stepType、workflowId、keyword 等 |
| get_workflow_step_detail | 按 id 获取单条完整记录,含完整 prompt、输出内容、result JSON 等字段。 | id |
| get_workflow_step_stats | 统计总数、success/failed/pending/running 数量、成功率、平均/最大耗时,并按状态、输出格式、provider、模型做分布汇总。 | 与 query 相同的可选过滤条件 |
1.3 测试结论
官方 MCP Client 冒烟测试通过:成功列出 3 个工具,真实库统计结果为 total=37、success=28、failed=3、pending=6、successRate=75.68%;分页查询与 id=121 详情查询均正常。
验证命令: node src\aiSkill\ai-mcp\scripts\mcp-smoke-test.mjs(在 servers 根目录运行)
二、Codex 如何调用这个 MCP 服务
2.1 注册方式
Codex 本身不会凭空知道该服务。需要把 stdio MCP 注册到 Codex 配置中,例如写入全局 config.toml 的 [mcp_servers] 段,或使用 codex mcp add。建议 command 使用 Node 绝对路径,并注入三个环境变量。
[mcp_servers.ai-workflow-step-mcp]
command = "C:/Program Files/nodejs/node.exe"
args = ["E:/otherProject/ai-video/service/servers/dist/main.js"]
env = { NODE_ENV = "development", MCP_STDIO_ENABLED = "true", AI_QUEUE_ENABLED = "false" }
命令行方式等价于:codex mcp add ai-workflow-step-mcp,并用 --env 注入上述环境变量,再以 -- 指定 node 与 dist/main.js。注册后需重启 Codex 或新建任务才会生效。
2.2 完整调用链路
1. Codex 作为 MCP Client,在会话启动时按配置 spawn 子进程:node ...\dist\main.js。
2. dist/main.js 检测到 MCP_STDIO_ENABLED=true,不启动 HTTP,而是创建 Nest 应用上下文并由 McpStdioTransportService 启动 stdio MCP Server。
3. 双方通过 stdin/stdout 走 JSON-RPC:先 initialize,再 tools/list,Codex 因此看到 3 个可用工具。
4. 用户用自然语言提问后,模型自行判断应调用哪个工具并生成参数。
5. MCP Client 发送 tools/call,Nest 侧由 TypeORM Repository 查询 MySQL ai_workflow_step 表。
6. 服务把结果封装为 CallToolResult(JSON 文本)返回,Codex 再整理成自然语言答案。
7. 会话关闭时传输随之关闭,进程退出;因此无需常驻端口,每次由 Codex 重新拉起。
2.3 使用示例
用户在CodeX提问:“ 最近有哪些视频生成步骤失败了?分别是什么错误?”
Codex 会调用 query_workflow_steps,参数大致为 { stepType: "video", stepStatus: "failed", page: 1, pageSize: 20 },服务按 create_date 倒序返回结果。
需要说明:如果 MCP 尚未注册进当前会话,Codex 不会主动拉起该服务。此前演示是用独立的官方 MCP Client 脚本发起相同协议调用,效果与 Codex 注册后一致。
三、业务查询示例:最近失败的视频生成步骤
按 step_type=video、step_status=failed、is_delete=0 查询并按创建时间倒序,当前共有 3 条失败记录。
| 记录 ID | 工作流 ID | 创建时间 (UTC) | 模型 / Provider | 错误结论 |
|---|---|---|---|---|
| 113 | 87 | 2026-08-31 11:45:14 | doubao-seedance-2-5-260628 / doubao-video | Doubao 404:账号未激活 doubao-seedance-2-5-260628,需在 Ark 控制台开通模型 |
| 112 | 86 | 2026-08-31 11:43:54 | MiniMax-H3 / minimax-video | MiniMax 请求失败:fetch failed,疑似网络或服务端不可达 |
| 108 | 82 | 2026-08-29 14:20:27 | doubao-seedance-2-5-260628 / doubao-video | Doubao 404:账号未激活 doubao-seedance-2-5-260628,需在 Ark 控制台开通模型 |
两类根因:
· 记录 113 与 108:豆包视频模型 doubao-seedance-2-5-260628 未在当前账号开通,需在火山方舟 Ark 控制台激活对应模型服务。
· 记录 112:MiniMax 请求网络失败(fetch failed),需要检查 MiniMax 侧服务状态、网络连通性及超时配置。
四、多系统接入与线上部署
4.1 能否给其它网页端 AI 聊天框调用
能,但不是网页前端直接连,而是聊天系统的后端作为 MCP Client 去调用。前提是服务需提供可被远程访问的 Streamable HTTP / SSE 端点;当前 stdio 模式只能被本机进程拉起。
· 传输层:stdio 只能本机使用;线上服务需增加 Streamable HTTP/SSE 端点,例如 https://域名/mcp。
· 调用方:对方聊天后端需接入 MCP Client SDK,负责 tools/list 与 tools/call;浏览器前端本身不直接通信。
· 安全:公开端点需鉴权、CORS 白名单、限流与审计;由于工具只读,风险相对可控。
· 替代方案:若只服务自家系统,直接提供只读 HTTP API 也可以,MCP 并非唯一通道。
4.2 tools/list 与 tools/call 的分层关系
可以按“AI 工具类调用”来理解,但要分清两层:模型负责决定调用哪个工具,MCP Client(对方后端)负责真正发送 tools/call。链路示意如下。
用户网页 → 对方 AI 后端(MCP Client) → 你的 MCP Server → MySQL → 结果返回 → LLM 汇总回答
4.3 对方使用 NestJS 与 nestjs-llm-tools 能否调用
不能仅靠 nestjs-llm-tools 直接调用 MCP 。
· nestjs-llm-tools(registry 当前版本 0.0.3)的作用是把 NestJS 本地方法定义成 LLM function calling 工具,适合做“本地工具注册”。
· 其依赖中没有 @modelcontextprotocol/sdk,因此它本身不是 MCP Client,无法连接并调用你的 MCP 服务。
· 正确接法:对方 NestJS 后端引入官方 MCP Client,用 client.listTools() 获取工具清单,将 schema 转给自家 LLM;模型选定后由后端执行 client.callTool(),再把结果回给模型。
五、鉴权方案:给每个接入系统发放密钥
可以,这也是私有部署最常见的方案:为每个接入方发放一把机器密钥(API Key/Bearer Token),请求时放入 Authorization 头。
· 一把密钥对应一个系统:便于单独吊销、限流与审计,避免所有系统共用同一密钥。
· 服务端只存哈希(bcrypt/scrypt 等),不存明文;密钥只在 HTTPS 通道传输。
· MCP 端点在执行任何请求前校验密钥,失败返回 401;成功后放行 tools/list 与 tools/call。
· Codex 注册远程 HTTP MCP 时可用 --bearer-token-env-var 指向环境变量,避免把明文密钥写进配置文件。
· 边界:机器密钥只能标识“哪个系统”,不能区分最终用户;若后续要做用户级数据隔离,需再叠加 JWT/OAuth 用户令牌。
六、Skill、角色与 MCP 的关系
6.1 Skill 能否替代 MCP
不能。 Skill 是给模型的“方法论/操作指南”,不具备进程管理或协议能力,不会自己拉起 MCP 服务,也不能替你完成 tools/list、tools/call。它可以指导模型“遇到这类问题优先用哪个 MCP 工具”,但底层仍需 MCP 已注册并连接。Codex Plugin 可以把 Skill 与 MCP 配置打包成可分发的插件,但运行时仍走 MCP。
6.2 Skill 是否代表“角色”
不准确。角色(Role)定义“以什么身份做、语气与边界”;Skill 定义“这类任务按什么流程做”;MCP 定义“能真正访问哪些外部能力”。三者互补而不是替代关系。
| 维度 | 角色( Role/Persona ) | Skill | MCP Server |
|---|---|---|---|
| 本质 | 身份与沟通方式 | 操作指南与领域流程 | 外部工具与数据通道 |
| 作用 | 决定以什么身份做 | 决定按什么方法做 | 决定能否真正调用外部系统 |
| 例子 | 资深 DBA / 审阅者 | 查询步骤与返回规范 | query_workflow_steps 等工具 |
七、结论与后续建议
· 当前 stdio 版 MCP 已能通过官方 MCP Client 调用,适合本机 Codex / 本地客户端接入;冒烟测试已验证工具列表与真实查询。
· Codex 接入需完成 config.toml 注册并重启/新开任务;注册后自然语言提问即可触发对应工具。
· 要服务多个线上系统,下一步应增加 Streamable HTTP/SSE 端点、每系统独立密钥鉴权、HTTPS、限流与审计。
· 对方 NestJS 后端接入需使用真正的 MCP Client;nestjs-llm-tools 只能用于本地 LLM 工具定义,不能替代 MCP 客户端。
· Skill、角色与 MCP 分属不同层次:Skill 负责方法,角色负责身份,MCP 负责外部能力连接,互相不能替代。