《MCP 详细讲解》把 Tools、Resources、Prompts 三类能力拆得很清楚,但很多人看完后卡在同一个地方:概念都懂了,下一步该让哪个客户端去调。这次我直接选 Codex 当客户端,用 TaoToken 做统一模型通道,先打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 创建 Key,再把三类能力一个个跑通。
注意,这里不是把 MCP 的理论再讲一遍,而是要把上一篇留下的实际操作缺口补上:Tools 到底是被谁调用的,Resources 怎么进到模型上下文,Prompts 又如何变成一个能选中的模板。文章里的 MCP Server 是完整单文件代码,存到本地就可以让 Codex 真正调起来。
1. MCP 是什么:这次选 Codex 当客户端
1.1 MCP 的三方角色怎么落到 Codex 上
MCP 把「模型连接外部系统」这件事拆成 Host、Client、Server 三个角色。之前只读概念时容易晕,落到 Codex 上就具体了:用户操作的是 Host,Codex 会拉起一条与 MCP Server 的连接,这条连接的维护者就是 Client;真正暴露能力的是 Server 进程。这个 Server 可以是本地 Node 进程,也可以是远程服务,我们这次用本地进程来演示。
模型本身不会主动去执行任何外部函数。它只是根据对话内容「建议」使用某个工具,真正发起调用的是客户端。换句话说,MCP 的能力要有人去拉,而 Codex 就是我们选的这个人。用户对 Codex 说「查一下东京天气」,Codex 判断需要调用 get_weather,接着把参数和调用意图发给 MCP Server,Server 执行完把结果返回,Codex 再让模型把结果整理成自然语言。这一整条链路里,模型从未直接接触天气 API。
1.2 准备材料:一张表看清 Key、Base URL、模型 ID
动手之前先对照这张表,三样东西分别填到不同的位置:Key 用来鉴权,Base URL 是 Codex 请求模型的接口地址,模型 ID 决定具体用哪个模型。
| 项目 | 取值 | 用在哪里 |
|---|---|---|
| API Key | YOUR_API_KEY(从 TaoToken 创建) | 环境变量 TAOTOKEN_API_KEY |
| Base URL | https://taotoken.net/api | ~/.codex/config.toml |
| 模型 ID | YOUR_MODEL_ID(以模型广场为准) | ~/.codex/config.toml |
Key 从 TaoToken 注册后创建,占位符统一写成 YOUR_API_KEY。Base URL 填给 Codex 时不要带 UTM 参数,也不要在末尾追加 /v1,直接写 https://taotoken.net/api。模型 ID 先不猜,文章里用 YOUR_MODEL_ID 占位,实际填写时去 TaoToken 模型广场复制真实 ID。这张表的分工清楚了,后面配置就不会把官网和接口混在一起。
2. Tools:先让 Codex 通过 TaoToken 调一次 get_weather
2.1 Tools 的实质是函数调用
Tools 是 MCP 里最像「函数调用」的能力。一个 Tool 通常包含名称、描述、输入参数结构和返回结果。模型根据上下文判断是否需要调用它,而不是由人手动触发。比如用户说「帮我查一下今天东京的天气」,模型可能会选择调用 get_weather,传入 city=Tokyo,工具执行后返回结构化结果,模型再把结果整理成人类可读的回答。
设计 Tools 时要注意它和普通函数的差异:调用可能产生外部动作。查询天气是只读的,相对安全;但如果一个 Tool 做的是发送邮件、删除文件、创建订单,就必须考虑权限、确认机制和审计日志。第一次跑通 MCP 链路时,建议先用只读工具练手,get_weather 就是很好的起点。
2.2 把 Codex 的模型通道指到 TaoToken
Codex 默认有自己的模型供应商配置,但我们用 TaoToken 把模型通道统一起来。编辑~/.codex/config.toml,加入下面这段内容:
# ~/.codex/config.toml model = "YOUR_MODEL_ID" # 从 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 模型广场复制,不要猜 model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" # 注意末尾不要加 /v1 env_key = "TAOTOKEN_API_KEY"然后在当前 shell 里导出环境变量:
export TAOTOKEN_API_KEY=YOUR_API_KEY这里有个容易搞混的点:官网地址是给人点的,上面已经加了 UTM;而base_url是给 Codex 用的接口地址,直接写 https://taotoken.net/api。两者不要互相替换。不同版本的 Codex 对env_key字段名可能有细微差异,以你本机安装版本的config.example.toml注释为准。
2.3 写一个同时暴露三类能力的 MCP Server
为了让 Tools、Resources、Prompts 都真实跑起来,我们在本地新建~/mcp-demo/server.js,用 MCP TypeScript SDK 写一个最小 Server。它同时暴露 get_weather 工具、users-schema 资源和 code_review_summary 模板,后面两章继续复用。
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod"; const server = new McpServer({ name: "demo-server", version: "0.1.0" }); server.tool( "get_weather", { city: z.string().describe("城市名,例如 Tokyo") }, async ({ city }) => ({ content: [{ type: "text", text: `${city}:多云,18°C,湿度 65%` }] }) ); server.resource( "users-schema", "db://schema/users", async (uri) => ({ contents: [{ uri: uri.href, text: "CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT UNIQUE, created_at TEXT);" }] }) ); server.prompt( "code_review_summary", { diff: z.string().describe("待评审的代码 diff") }, ({ diff }) => ({ messages: [{ role: "user", content: { type: "text", text: `请按以下结构做代码评审:\n\n1. 变更概览\n2. 潜在问题\n3. 测试建议\n4. 优化建议\n\n代码 diff:\n${diff}` } }] }) ); await server.connect(new StdioServerTransport());初始化并安装依赖:
mkdir -p ~/mcp-demo cd ~/mcp-demo npm init -y npm install @modelcontextprotocol/sdk zod接着把 MCP Server 注册进 Codex。在~/.codex/config.toml末尾追加:
[mcp_servers.demo] command = "node" args = ["/Users/you/mcp-demo/server.js"]如果你用 nvm 管理 Node,command最好写成node的绝对路径,否则 Codex 可能找不到 PATH 里的命令。改完配置后重启 Codex 会话。
2.4 验证 Tools 是否真的被 Codex 调起来
在 Codex 会话里输入「今天东京天气怎么样」,观察它的回应。正常情况下 Codex 会调用 get_weather,传入 city=Tokyo,拿到 Server 返回的天气文本后组织回答。如果看不到调用动作,可以追问一句「你刚才调用了哪个工具?」让 Codex 把决策过程暴露出来。
这一步验证的不只是 MCP Server 本身,而是整条链路的连通性:Codex 通过 TaoToken 拿到模型响应,模型决定调用工具,Codex 执行 MCP 调用,结果再回到模型进行表达。任何一环断了,都会在这里暴露。
3. Resources:把 db://schema/users 读进 Codex 的上下文
3.1 Resources 为什么不是另一种 Tools
Resources 可以理解为 MCP Server 提供给客户端读取的上下文数据。它不像 Tools 那样强调执行动作,更像是「可读取的文件、记录或数据源」。项目 README、数据库 schema、日志文件、用户日程都可以用 Resource 暴露。通常用 URI 标识,比如 file:///project/README.md、db://schema/users。
Resources 的核心价值是让模型获得更准确的背景信息。比如你在 IDE 里问「这个函数为什么报错」,MCP Server 可以把相关源代码、测试结果、依赖信息作为 Resources 提供给模型。模型不需要猜测,而是基于真实上下文分析问题。和 Tools 相比,Resources 更适合只读数据,它的设计重点不是「执行」,而是「提供背景」。
3.2 server.js 里的 Resource 长什么样
前面 server.js 里已经注册了一个名为users-schema的 Resource,URI 是db://schema/users。它返回的 text 是一段建表语句。当 Codex 需要了解 users 表结构时,可以通过这个 URI 把文本放入模型上下文。
db://schema/users CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT UNIQUE, created_at TEXT);这里有个实际建议:刚开始接 MCP 的 Resources 时,不必一上来就让 Server 直连生产数据库。先用静态文本模拟 schema,验证 Codex 能读到、能理解、能基于它回答,再逐步把 text 替换成真实查询结果。这样出问题时,至少能确定是读取问题还是查询问题。
3.3 在 Codex 里触发一次 Resources 读取
在 Codex 会话里输入「users 表结构是什么?帮我分析 email 为什么可能重复」,观察它是否会读取db://schema/users。如果 Codex 没有自动读取,直接在对话里要求「先读取 db://schema/users,再回答」。一旦 schema 文本进入上下文,Codex 就能发现 email 字段带 UNIQUE 约束,并指出重复可能来自大小写、NULL 或历史数据导入等方向。
提示:这段 schema 只是文本,Codex 不会自己连数据库。要核对生产库里真实存在的重复 email,请在 SQL*Plus 或数据库客户端里执行查询,再把结果贴回对话,让 Codex 帮你对照分析。
4. Prompts:把代码评审模板做成可复用入口
4.1 Prompts 与 Tools、Resources 的触发差异
Prompts 是 MCP Server 暴露的可复用提示模板,可以把某类固定工作流封装起来。Tools 通常由模型根据上下文决定是否调用,Resources 通常由客户端决定如何附加上下文,Prompts 则更像菜单、快捷命令或工作流入口,帮助用户以标准方式完成高频任务。
比如团队经常需要生成代码评审总结,就可以提供一个 code_review_summary Prompt。它要求输入代码 diff、项目背景和关注点,然后生成结构化的评审意见。和写死一段提示词相比,Prompts 胜在结构稳定:所有使用者拿到的都是同一套框架,输出粒度相对可控。
4.2 在同一个 MCP Server 里注册 Prompt
继续用前面的 server.js,里面的server.prompt方法注册了 code_review_summary 模板。它接收diff参数,把用户消息整理成带四个固定小节的评审请求:
server.prompt( "code_review_summary", { diff: z.string().describe("待评审的代码 diff") }, ({ diff }) => ({ messages: [{ role: "user", content: { type: "text", text: `请按以下结构做代码评审:\n\n1. 变更概览\n2. 潜在问题\n3. 测试建议\n4. 优化建议\n\n代码 diff:\n${diff}` } }] }) );注意,这个模板不会自动改变模型行为,它只负责把用户消息整理成固定格式。实际生成评审意见的还是模型。也就是说,Prompts 直接把「原材料」喂给模型,减少它自由发挥的余地,但最终回答质量仍取决于模型本身。
4.3 在 Codex 里实际调用模板
在 Codex 会话里粘一段代码 diff,然后说「用 code_review_summary 模板评审这个 diff」。Codex 会读取模板内容,按四个小节输出评审意见。如果输出没有出现「变更概览」「潜在问题」等结构,多半是模板没被加载。检查~/.codex/config.toml里的mcp_servers路径是否正确,然后重启 Codex 会话。
这一步跑通后,Prompts 的价值就很直观了:它把「每次都要重复交代评审格式」变成「只要说出模板名,格式自动固定」。团队里其他人用 Codex 时,也可以共用同一个 MCP Server 里的模板,评审口径不会各写各的。
5. 三者怎么区分:一场「出差规划」看三种能力的分工
5.1 一次出差里的三类能力
用一个生活场景把三者串起来:用户说「帮我规划一次出差」。Resources 提供日程、预算规则、历史出差偏好,模型不需要凭记忆猜用户的住宿习惯;Prompts 提供「出差规划」模板,规定模型先收集哪些信息、按什么结构输出;Tools 则负责查航班、订酒店、建日程。三者配合,才能形成完整的智能工作流。
如果只把「查航班」做成 Tool,模型能飞航班,但没有预算规则和日程偏好,规划结果就是空泛的。如果只把预算规则做成 Resource,模型了解背景却没有执行能力,规划只能停在纸面。如果只把出差流程做成 Prompt,模型知道步骤却不知道当前时间和目的地,模板也无法落地。三类能力本身是互补的。
5.2 TaoToken 在这条链路里的真实位置
TaoToken 并不替代 MCP Server,也不替代 Codex。它负责的是「模型通道」这一层:Codex 需要向某个 OpenAI 兼容接口发送请求,TaoToken 把接口统一,Codex 通过它发起的模型请求会在 TaoToken 账号下产生 Token 消耗。MCP Server 完全不需要知道模型是从哪来的,它只负责把工具、资源和模板暴露出来。
完整链路是这样的:用户在 Codex 提问,Codex 把请求发到 https://taotoken.net/api,模型返回回答或工具调用意图;Codex 接着调 MCP Server 执行 get_weather,把结果拿回来交给模型整理;最终输出给用户。这条链路里有三层东西各司其职:TaoToken 负责通道,Codex 负责编排和消耗 Token,MCP Server 负责暴露能力与上下文。以后排障时也按这个分层去看问题,不会一头扎进代码里找不到方向。
6. 排障与设计建议:别把数据库 schema 硬塞给 Tools
6.1 Codex 配置常见问题
按上面的配置走完,最可能遇到三个问题。
第一,401 Unauthorized。检查环境变量是否真的导出了:echo $TAOTOKEN_API_KEY看看是不是 YOUR_API_KEY。如果 Key 复制不完整,去官网重新创建。第二,model not found。YOUR_MODEL_ID 只是占位符,不代表真实模型 ID,Codex 会直接报错。去 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 的模型广场复制一个实际存在的 ID 填回来。第三,MCP Server 没被加载。改完~/.codex/config.toml之后没重启会话,或者args里的路径写错,都会造成 Codex 看不到工具。用绝对路径最稳妥。
6.2 按边界拆能力,而不是把所有东西都塞进 Tools
开发 MCP Server 时,不要把所有能力都塞进 Tools。很多数据其实只需要被读取,做成 Resources 会更清晰、更安全;很多固定任务也不一定需要写代码逻辑,做成 Prompts 反而更容易维护。Tools 要强调权限和副作用控制,Resources 要保证数据新鲜度和访问范围,Prompts 要保持结构稳定,避免依赖模糊的大段提示词。
放到这次 Codex 场景里,设计建议可以具体化成四条:第一次跑通链路时只用只读工具,权限问题先不碰;Resources 先给静态文本,验证读取正常后再接真实数据源;Prompts 的输出结构写成固定小节,方便后续对照模板排障;涉及生产库的操作由人在本地执行,Codex 只负责生成 SQL 和解释结果,不代跑线上命令。
等你在 Codex 里依次处理过天气、schema 和代码评审三个请求,再回头看 MCP 官方文档,三类能力就不再是三个名词,而是三条可以分开验证的链路。如果还没建 Key,先去 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 创建一个,再回来把 server.js 跑起来;跑通后回到同一页面,看看这次 Codex 调用产生的 Token 记录,你会对每一层做了什么有更直观的感受。