
1. 从“只会聊天”到“动手做事”中间差了什么大语言模型能写诗、能改代码、能陪你聊一整天但你让它“帮我查一下数据库里昨天的订单量”它只能礼貌地告诉你它做不到。原因不复杂模型训练完之后知识就冻结在参数里了它没法主动去读你的文件、调你的接口、碰你的数据库。Function Calling 在 2023 年补上了这块短板——你写一个函数把函数名、参数格式、描述打包发给模型模型判断需要调用时吐出结构化 JSON你的程序执行完再把结果塞回去模型据此生成自然语言回答。这套流程跑通之后模型确实“能动手”了。但真把它放进项目里问题就来了。Function Calling 的函数定义通常跟具体模型服务绑死换一家模型供应商工具描述格式可能就得重写三个项目都要用同一个“查天气”函数你得复制三份实现语言不一致还得翻译一遍团队里 A 用 Python 写工具、B 用 TypeScript 写工具调用方式各玩各的代码库碎成一地。MCPModel Context Protocol模型上下文协议就是冲着这些痛点来的它把工具调用从“写死在应用里”解耦成“独立服务”用一套开放协议规定好 Server 怎么暴露能力、Client 怎么发现和调用做到一次开发、多处复用。这篇就带你从零把 MCP 服务端和客户端配起来跑通一条真实的工具调用链路让你手里的模型从“能聊天”升级成“能干活”。2. 前置准备TaoToken 接入与 MCP 运行环境MCP 本身是协议层的东西它不绑定任何一家模型服务。但你要验证“模型能不能正确发起工具调用”就得有一个能响应 Function Calling / Tool Use 的模型端点。我这边用 TaoToken 来做模型接入层原因是它同时提供 OpenAI 兼容接口和 Claude Code 兼容接口MCP 客户端配置里改个 base_url 就能切换省得为了测一个协议去折腾多套鉴权。先拿 Key。打开 https://taotoken.net/api-keys 登录后创建一个 API Key复制出来存好后面客户端配置里要用。注意这个 Key 只在创建时完整显示一次关掉页面就看不到了建议直接贴进密码管理器。模型对话调试入口在 https://taotoken.net/model-chat 你可以在网页里先发一条带工具定义的请求确认模型能吐出 tool_calls 结构再去配本地 MCP 客户端这样排障时能快速区分是“模型不响应工具调用”还是“MCP 链路配置错了”。如果你打算长期跑编码类 Agent比如让模型通过 MCP 读写本地文件、执行命令可以看下 Coding Planhttps://taotoken.net/coding-plan 它针对高频编码场景做了额度优化比按量计费更适合天天跑 Agent 的人。接入文档在 https://taotoken.net/doc 里面列了 OpenAI 兼容端点和 Anthropic 兼容端点的具体路径差异配 MCP 客户端时对着改就行。API 基础地址统一用 https://taotoken.net/api 不要加多余路径具体端点拼接方式文档里有表格。环境方面你需要Node.js 18大部分 MCP Server 是 npm 包用 npx 直接跑Python 3.10如果你要写 Python 版 MCP Server一个支持 MCP 的客户端比如 Claude Desktop、Cursor或者自己写一个基于 mcp SDK 的 Client3. 可复制配置MCP 服务端 config.toml 骨架MCP Server 的职责是暴露工具。下面这个 config.toml 骨架定义了一个本地文件读取 Server提供两个工具read_file 和 list_dir。你可以直接复制改掉 root 路径就能用。# mcp-server-filesystem/config.toml [server] name local-filesystem version 0.1.0 description 提供本地文件读取与目录列举能力的 MCP Server [transport] # stdio 模式客户端通过标准输入输出与 Server 通信适合本地进程 type stdio command npx args [-y, modelcontextprotocol/server-filesystem, /Users/yourname/workspace] [capabilities] tools true resources true prompts false [[tools]] name read_file description 读取指定路径的文本文件内容返回 UTF-8 字符串 [tools.inputSchema] type object properties.path { type string, description 相对于 workspace 根目录的文件路径例如 docs/readme.md } required [path] [[tools]] name list_dir description 列出指定目录下的文件和子目录名称 [tools.inputSchema] type object properties.path { type string, description 相对于 workspace 根目录的目录路径例如 src } required [path] [limits] max_file_size_kb 512 allowed_extensions [.md, .txt, .json, .toml, .py, .ts]几个关键点解释一下。transport.type 选 stdio 是因为本地开发最省事客户端拉起 Server 进程后直接走管道通信不用开端口。command 和 args 是客户端启动 Server 时执行的命令这里用 npx 拉官方 filesystem server最后一个参数是允许访问的根目录务必改成你自己的路径别写/或者用户主目录否则模型能读到你所有文件。capabilities 里 tools true 表示这个 Server 暴露工具能力resources true 表示还暴露资源比如可以直接把某个文件作为 context 注入prompts false 表示不提供预置提示词模板。limits 是我自己加的约束段官方 server 不一定认这个字段但你在自研 Server 里可以实现它用来限制单文件大小和允许的扩展名防止模型一口气读个几百 MB 的日志把上下文撑爆。如果你要写自己的 MCP ServerPython 侧最小骨架长这样# my_mcp_server.py from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent app Server(my-tools) app.list_tools() async def list_tools(): return [ Tool( nameget_order_count, description查询指定日期的订单数量, inputSchema{ type: object, properties: { date: {type: string, description: 日期格式 YYYY-MM-DD} }, required: [date] } ) ] app.call_tool() async def call_tool(name: str, arguments: dict): if name get_order_count: date arguments[date] # 这里替换成你真实的数据库查询 count 1024 return [TextContent(typetext, textf{date} 的订单量为 {count})] raise ValueError(f未知工具: {name}) async def main(): async with stdio_server() as (read, write): await app.run(read, write, app.create_initialization_options()) if __name__ __main__: import asyncio asyncio.run(main())装依赖用pip install mcp跑起来用python my_mcp_server.py它就会在 stdio 上等客户端发初始化请求。4. 客户端 settings.json 配置与工具调用链路验证客户端这边以 Claude Desktop 风格的 settings.json 为例其他 MCP 客户端字段名可能略有差异但结构一致。{ mcpServers: { local-filesystem: { command: npx, args: [ -y, modelcontextprotocol/server-filesystem, /Users/yourname/workspace ], env: { MCP_LOG_LEVEL: info } }, my-order-tools: { command: python, args: [/Users/yourname/mcp-servers/my_mcp_server.py], env: { DB_HOST: 127.0.0.1, DB_PORT: 5432 } } }, model: { provider: openai-compatible, baseUrl: https://taotoken.net/api, apiKey: sk-你的TaoToken密钥, modelName: claude-sonnet-4-20250514 } }mcpServers 下每个键是一个 Server 实例名客户端启动时会并行拉起这些进程。command args 跟服务端 config.toml 里写的启动命令对应。env 可以传环境变量比如数据库连接信息注意别把密钥硬编码进 args 里放 env 相对安全一点但生产环境还是建议走密钥管理服务。model 段是模型接入配置baseUrl 填 https://taotoken.net/api apiKey 填你刚才创建的 KeymodelName 填你要用的模型标识。如果你的客户端走 Anthropic 兼容协议baseUrl 和鉴权头格式按接入文档调整。配好之后重启客户端它会在启动时向每个 Server 发送 initialize 请求Server 返回自己支持的能力列表。你可以在客户端日志里看到类似这样的输出[mcp] server local-filesystem initialized, capabilities: tools, resources [mcp] server my-order-tools initialized, capabilities: tools [mcp] discovered 3 tools: read_file, list_dir, get_order_count接下来做一次真实调用验证。在对话里输入“帮我看看 workspace 下 docs 目录里有哪些文件然后读一下 readme.md 的前 200 个字。”模型收到请求后会先发起 list_dir 调用{ type: tool_use, id: toolu_01ABC, name: list_dir, input: { path: docs } }客户端拦截到这个 tool_use转发给 local-filesystem ServerServer 执行后返回{ type: tool_result, tool_use_id: toolu_01ABC, content: [{ type: text, text: readme.md\napi.md\nchangelog.md }] }客户端把结果回传给模型模型接着发起 read_file 调用拿到内容后生成最终回答。整条链路跑通说明 MCP 配置没问题。如果模型只回“我无法访问文件系统”那大概率是 Server 没启动成功或者工具列表没被发现去看客户端日志里有没有 initialize 失败的报错。5. 本篇常见错排查Server 启动即退出日志显示 command not found。客户端拉起 Server 时用的 PATH 可能跟你终端里不一样。npx 找不到就写绝对路径比如/usr/local/bin/npx。Python 同理用which python查出来填进去。工具列表为空但 Server 进程活着。检查 Server 的 capabilities 声明。有些 Server 默认不暴露 tools需要在启动参数里加--enable-tools之类的 flag。另外确认客户端版本支持 MCP老版本可能只认 resources 不认 tools。模型不发起工具调用直接编答案。两个原因一是模型本身对 tool_use 支持不好换个支持 Function Calling 的模型试二是工具 description 写得太模糊模型判断不出什么时候该用。把 description 写具体比如“查询指定日期的订单数量”比“查订单”好得多参数 description 也要写清楚格式示例。调用返回 401 或 403。检查 apiKey 有没有多余空格baseUrl 是不是写成了https://taotoken.net/api/带尾斜杠有些客户端对尾斜杠敏感。另外确认 Key 没有过期或被禁用去 console 里看一眼状态。stdio 通信卡死请求发出去没响应。常见于 Server 往 stdout 打了非协议内容比如 print 调试信息。MCP 走 stdio 时 stdout 只能传协议 JSON调试日志必须走 stderr。检查你的 Server 代码里有没有裸 print。文件读取报 permission denied。Server 启动时传入的根目录参数决定了它能访问的范围模型请求的路径如果解析后超出这个范围会被拒绝。确认你传的根目录包含了目标文件并且路径拼接时没有../逃逸。6. 把 MCP 接进你的日常工具链配通一次之后后面就是复制粘贴改路径的事。我自己的做法是把常用的 MCP Server 分成两类一类是通用能力文件系统、HTTP 请求、SQLite 查询直接跑官方实现另一类是业务专属查内部订单、读配置中心自己写 Server 暴露成工具。客户端 settings.json 里把这两类都挂上模型就能在一个对话里同时调文件、查库、发请求。想让模型侧调试更顺手可以去 https://taotoken.net/model-chat 里手动构造带 tools 的请求观察模型返回的 tool_calls 结构是否符合预期确认没问题再写进客户端配置。接入细节和端点差异查 https://taotoken.net/doc Key 管理在 https://taotoken.net/api-keys 长期跑编码 Agent 的话 Coding Plan 在 https://taotoken.net/coding-plan 。Claude Code 相关的 Anthropic 兼容配置参考 https://taotoken.net/claude-code-anthropic 控制台入口在 https://taotoken.net/console 。一个实用技巧给每个 MCP Server 的日志加个前缀比如[fs]、[order]客户端日志混在一起时能快速定位是哪个 Server 出的问题。另外工具数量别一次挂太多模型在几十个工具里选容易选错按场景分组、用的时候再启用对应的 Server准确率会高不少。