ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MCP 与 FC 之比较:从 Function Calling 到 Model Context Protocol 的工具调用演进

MCP 与 FC 之比较:从 Function Calling 到 Model Context Protocol 的工具调用演进 1. 从一次 Agent 工具链选型说起MCP 与 Function Calling 到底差在哪如果你正在做 AI Agent 的工具链选型大概率会在某个深夜盯着屏幕纠结Function Calling 已经能跑通了为什么还要引入 MCPMCP 听起来更标准但多一层 Server 是不是纯属给自己找麻烦我先把结论摆在前面Function Calling下称 FC解决的是模型能不能调用工具MCPModel Context Protocol模型上下文协议解决的是不同模型和不同工具之间怎么用同一套标准对接。前者是模型的原生能力后者是模型与工具之间的通用协议层。它们不是替代关系而是分工关系。FC 的典型形态是你在请求里带上一个tools数组里面是 JSON Schema 描述的函数签名模型判断需要调用时返回一个结构化的tool_calls你执行完把结果塞回对话模型再继续生成。整个过程没有中间层链路是用户 → 模型 → 你的代码 → 工具 API。MCP 的典型形态是你把工具按协议封装成一个 MCP Server通过 stdio 或 Streamable HTTP 暴露出去任何兼容 MCP 的客户端Claude Desktop、Cline、Cursor、Claude Code 等都能发现并调用这些工具。链路变成用户 → 模型 → MCP Client → MCP Server → 工具 API多了一层协调层。这篇文章面向正在选型的开发者我会用可复制的配置和可复现的验证步骤把两条路线都跑一遍。你会看到同一个查询订单状态的工具用 FC 怎么写、用 MCP 怎么写各自的配置文件长什么样请求发出去之后返回什么以及踩坑时那些报错到底在说什么。读完你应该能自己判断下一个项目该用 MCP还是 FC 已经够了。需要说明的是本文所有调用示例都通过统一的 API 入口发起Base URL 使用https://taotoken.net/api这样无论你最终选 FC 还是 MCP模型侧的接入方式是一致的方便对比。2. 前置准备用 TaoToken 统一模型入口再决定 FC 还是 MCP在对比两种工具调用方式之前得先有一个能稳定发起模型请求的入口。否则你会在模型连不上和工具调不通两个问题之间反复横跳根本分不清是协议的问题还是网络的问题。我的做法是先把模型入口固定下来用 TaoToken 作为统一的 API 网关。它的控制台在https://taotoken.net/consoleAPI Key 在https://taotoken.net/api-keys生成。生成之后你会拿到一个形如sk-xxxxxxxx的密钥后面 FC 和 MCP 两种方式都会用到它。为什么强调统一入口因为 FC 和 MCP 的差异在工具层不在模型层。如果你 FC 用一个厂商的 SDK、MCP 又换另一套鉴权最后排查问题时变量太多。把 Base URL 固定成https://taotoken.net/api模型侧的行为就一致了你观察到的差异就纯粹来自工具调用机制本身。具体操作上先在 API Keys 页面创建一个密钥建议按用途命名比如fc-demo和mcp-demo各一个方便后面看调用量时区分。创建后立刻复制保存页面刷新后就不再完整显示。然后确认你要用的模型 ID。FC 场景下模型需要支持tools参数主流模型基本都支持MCP 场景下模型本身不直接感知 MCP是客户端在中间做转换所以模型只要支持标准的对话补全即可。这一点很关键MCP 并不要求模型懂 MCP 协议协议是客户端和 Server 之间的事。如果你打算长期做 Agent 开发建议顺手看一下 Coding Plan 的说明https://taotoken.net/coding-plan它更适合高频编码和 Agent 场景额度和计费方式跟按次调用不太一样。选型阶段先用按次调用验证逻辑跑通之后再考虑套餐。环境变量建议这样设置后面所有示例都基于它export TAOTOKEN_API_KEYsk-你的密钥 export TAOTOKEN_BASE_URLhttps://taotoken.net/apiPython 侧安装依赖pip install openai mcpopenai包用来演示 FCmcp包用来写 MCP Server。两个都装上方便你在同一台机器上对比。这里有个容易忽略的点MCP 的 Python SDK 要求 Python 3.10 以上如果你本地是 3.8 或 3.9pip install mcp会报版本不兼容。先用python --version确认一下不够就升级别在这个环节卡住。3. 可复制配置FC 的 tools 数组与 MCP Server 的完整写法这一节是全文的核心我会把两种方式的完整配置都贴出来你可以直接复制到本地跑。先看 FC。3.1 Function Calling 的 tools 配置FC 的核心是把工具描述成 JSON Schema随请求下发。下面是一个查询订单状态的工具定义import os from openai import OpenAI client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlos.environ[TAOTOKEN_BASE_URL], ) tools [ { type: function, function: { name: query_order_status, description: 根据订单号查询订单的当前状态返回已支付、已发货、已签收等状态, parameters: { type: object, properties: { order_id: { type: string, description: 订单号形如 ORD20240101001, } }, required: [order_id], }, }, } ] response client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: 帮我查一下订单 ORD20240101001 的状态}], toolstools, tool_choiceauto, ) print(response.choices[0].message.tool_calls)这段代码跑通后你会看到模型返回一个tool_calls列表里面包含函数名和参数。注意tool_choiceauto表示让模型自己决定要不要调用工具如果你确定这轮必须调用可以设成{type: function, function: {name: query_order_status}}强制指定。FC 的配置特点很鲜明工具定义和请求绑在一起每次请求都要带上完整的tools数组。工具多了之后这个数组会变得很长token 消耗也会上去。这是 FC 的固有成本后面讲性能时会再提。3.2 MCP Server 的完整写法MCP 的思路完全不同工具定义一次注册到 Server之后任何兼容的客户端都能发现它。用 Python SDK 写一个最小 Serverfrom mcp.server.fastmcp import FastMCP mcp FastMCP(order-service) mcp.tool() def query_order_status(order_id: str) - str: 根据订单号查询订单的当前状态。 Args: order_id: 订单号形如 ORD20240101001 fake_db { ORD20240101001: 已发货, ORD20240101002: 已签收, } return fake_db.get(order_id, 订单不存在) if __name__ __main__: mcp.run(transportstdio)保存为order_server.py然后配置客户端。以 Claude Desktop 为例配置文件在~/Library/Application Support/Claude/claude_desktop_config.jsonmacOS内容如下{ mcpServers: { order-service: { command: python, args: [/绝对路径/order_server.py], env: { TAOTOKEN_API_KEY: sk-你的密钥 } } } }如果你用的是 Cline 或 Claude Code配置位置不同但结构一致都是mcpServers下面挂一个服务名指定command、args、env。这里必须写绝对路径相对路径在客户端启动子进程时经常找不到文件这是新手最容易踩的坑。对比一下就很清楚了FC 的工具定义是每次请求携带MCP 的工具定义是一次注册、长期可用。FC 的配置散落在业务代码里MCP 的配置集中在客户端的 JSON 文件里。前者灵活但重复后者规范但需要多一层进程管理。3.3 三件套对照Base URL、Key、Model ID无论走哪条路模型侧的三件套都要对齐。用表格对照一下项目FC 方式MCP 方式Base URLhttps://taotoken.net/apihttps://taotoken.net/api由客户端调用模型时使用API KeyTAOTOKEN_API_KEY环境变量写入 MCP 客户端配置的envModel ID请求里显式指定如gpt-4o-mini由客户端配置决定Server 本身不感知关键区别在于FC 里模型 ID 是你代码里写死的MCP 里模型 ID 是客户端配置的MCP Server 完全不知道背后用的是哪个模型。这正是 MCP 解耦的价值——同一个 Server换个客户端、换个模型照样能用。4. 验证请求从 tool_calls 到 MCP 工具列表的成功返回配置写完得验证它真的能跑。这一节给你两条可复现的验证路径。4.1 验证 FC拿到 tool_calls 并回填结果接着 3.1 的代码完整跑一轮调用 → 执行 → 回填import json messages [{role: user, content: 帮我查一下订单 ORD20240101001 的状态}] response client.chat.completions.create( modelgpt-4o-mini, messagesmessages, toolstools, tool_choiceauto, ) msg response.choices[0].message messages.append(msg) if msg.tool_calls: for call in msg.tool_calls: args json.loads(call.function.arguments) # 这里替换成你真实的业务查询 result f订单 {args[order_id]} 当前状态已发货 messages.append({ role: tool, tool_call_id: call.id, content: result, }) final client.chat.completions.create( modelgpt-4o-mini, messagesmessages, toolstools, ) print(final.choices[0].message.content)成功的话最后会打印类似订单 ORD20240101001 当前状态为已发货的自然语言回复。注意tool_call_id必须和模型返回的call.id严格对应写错了会报 400提示 tool 消息找不到对应的调用。4.2 验证 MCP用 Inspector 看工具列表MCP 的验证更直观官方提供了 Inspector 工具npx modelcontextprotocol/inspector python /绝对路径/order_server.py执行后浏览器会打开一个调试界面左侧能看到order-service这个 Server点开 Tools 标签应该能看到query_order_status及其参数 schema。在界面里填入order_id为ORD20240101001点运行右侧会返回已发货。这一步能跑通说明 Server 本身没问题。接下来在 Claude Desktop 或 Cline 里重启客户端在对话里问查一下订单 ORD20240101001客户端会自动发现工具、发起调用、把结果回填给模型。你会在界面上看到工具调用的折叠块展开能看到入参和返回值。两条路径的验证重点不同FC 验证的是模型有没有正确生成 tool_callsMCP 验证的是客户端有没有正确发现并调用 Server 的工具。前者出问题多半在 schema 描述后者出问题多半在配置路径或进程启动。4.3 成功结果的判断标准FC 成功的标志response.choices[0].message.tool_calls非空且function.name等于你定义的工具名arguments是合法 JSON。MCP 成功的标志Inspector 里能看到工具列表调用返回预期结果客户端对话里出现工具调用记录且最终回复引用了工具返回的数据。如果 FC 返回的tool_calls是None说明模型认为不需要调用工具通常是description写得太模糊或者用户问句和工具能力不匹配。如果 MCP 在客户端里看不到工具先检查配置文件路径和 Python 解释器路径再看客户端日志。5. 常见报错排查401、local proxy failed、reading choices、OAuth这一节把两条路线最常见的报错集中处理。这些错误我在实际项目里都遇到过按顺序排查基本能定位。5.1 401 UnauthorizedFC 场景下出现 401九成是 API Key 没读到。检查os.environ[TAOTOKEN_API_KEY]是否真的存在有时候你在终端export了但 IDE 里的运行环境没继承。建议在代码里加一行print(os.environ.get(TAOTOKEN_API_KEY, NOT SET)[:8])确认前几位。MCP 场景下出现 401通常是客户端配置的env里没传 Key或者 Key 写错了。注意 MCP 客户端启动 Server 是独立进程它不会继承你终端的 shell 环境变量必须在 JSON 配置的env字段里显式写。5.2 local proxy failed这个报错一般出现在客户端尝试连接 MCP Server 时。含义是客户端启动 Server 子进程失败。常见原因有三个command写的python在客户端的环境里不存在换成绝对路径如/usr/bin/python3args里的脚本路径是相对路径换成绝对路径脚本本身有语法错误启动即崩溃。排查方法把配置里的command和args拼成一条命令在终端里手动执行一遍。如果终端能跑、客户端跑不了就是环境变量或路径的问题。5.3 reading choices 报错TypeError: Cannot read properties of undefined (reading choices)这类错误通常发生在你直接取response.choices但response本身是错误对象的时候。根因往往是请求根本没成功返回的是错误结构。先打印完整的response看内容再对照状态码。常见触发是模型 ID 写错或者tools数组格式不合法导致请求被拒。5.4 OAuth 相关报错如果你接的 MCP Server 是远程的、需要 OAuth 鉴权可能会遇到invalid_grant或token expired。这类问题不在模型侧而在 Server 的鉴权配置。检查 client_id、client_secret、回调地址是否和 Server 注册的一致。本地 stdio 的 Server 一般不涉及 OAuth遇到这个报错说明你用的是远程 Server。5.5 排查顺序建议遇到问题按这个顺序走先确认模型请求本身能通用最简单的对话补全测再确认工具定义格式正确最后确认客户端与 Server 的进程通信正常。把模型问题和工具问题分开排查效率会高很多。6. 选型结论与下一步什么时候用 MCP什么时候 FC 就够把前面的内容收拢成一张决策表判断维度选 FC选 MCP工具数量少量、固定多、持续增加模型数量单一模型多模型复用团队规模个人或小团队多人协作、需要统一标准部署形态单机脚本需要独立进程、可被多客户端发现性能要求极低延迟、单次短请求可接受一层中转安全合规一般场景敏感数据需本地化、需权限管控一句话判断如果你只是给一个模型加几个工具、跑在单机脚本里FC 完全够用别为了标准而标准。如果你要让多个客户端、多个模型共用同一批工具或者工具会持续增加MCP 的注册式设计会帮你省下大量重复适配。融合使用也是成熟做法把稳定的、需要复用的工具封装成 MCP Server把一次性的、性能敏感的调用留在 FC 里。两者并不冲突客户端完全可以在同一轮对话里既走 MCP 工具又走原生 FC。下一步建议你动手做两件事一是把本文的order_server.py改成你自己的业务工具在 Inspector 里验证一遍二是用 FC 的方式实现同一个工具对比两者的代码量和维护成本。跑完这两步选型结论对你来说就不再是纸面分析而是有体感的判断。模型入口统一用https://taotoken.net/apiKey 在https://taotoken.net/api-keys生成接入细节可以对照https://taotoken.net/doc。如果你打算把 Agent 跑成长期任务Coding Plan 的额度模型值得提前了解想先直观感受模型对工具调用的理解能力也可以直接在模型对话里试几轮。工具调用的演进不会停在 FC 或 MCP 任何一边但把这两条路都亲手跑通的人在下一轮变化来临时会从容得多。
返回列表