ARTICLE DETAIL

资讯详情

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

从“小酌”视角拆解MCP技术架构:LLM与Function Calling如何通过SSE/HTTP协同

从“小酌”视角拆解MCP技术架构:LLM与Function Calling如何通过SSE/HTTP协同 1. 从一个真实困惑说起MCP 到底在 LLM 工具调用链路里干了什么第一次认真看 MCP 的时候我脑子里其实有个很具体的疑问LLM 早就有 Function Calling 了模型自己就能输出一个带参数的 JSON 说“我要调 get_weather”为什么还要在中间塞一个 MCP它和 SSE、HTTP 又是什么关系这三个词经常被放在一起讲但真正落到代码里很多人是懵的。先把结论摆出来方便你带着框架往下看。MCPModel Context Protocol是一套开放协议它把“应用程序怎么把上下文和工具提供给 LLM”这件事标准化了。你可以把它理解成 AI 应用的 USB-C 接口以前每个模型厂商、每个 Agent 框架都自己定义一套工具描述格式接一个工具就要写一遍适配MCP 想做的就是让工具提供方和工具使用方按同一套接口对话。它本身不是模型能力也不是 Function Calling 的替代品而是 Function Calling 的“插座标准”。那 Function Calling 是什么它是 LLM 的一项基础能力模型在推理时能根据你给它的工具列表判断该不该调工具、调哪个、参数填什么然后输出一段结构化的调用请求。注意模型只负责“决定”和“生成参数”真正执行工具的是外面的程序。这个执行者在 MCP 架构里就是 MCP Client。SSE 和 HTTP 则是传输层的事。MCP 早期远程通信用 SSEServer-Sent Events后来扩展了 Streamable HTTP。SSE 的特点是服务器可以持续往客户端推数据适合流式返回。HTTP 则是我们最熟悉的请求-响应模式。MCP 的本地工具走 Stdio远程工具走 SSE 或 Streamable HTTP这就是它传输方式的三种形态。所以整条链路大概是LLM 通过 Function Calling 决定调哪个工具 → MCP Client 把工具列表告诉 LLM、并解析模型的调用请求 → MCP Client 通过 SSE/HTTP 把请求发给 MCP Server → Server 执行真实工具 → 结果沿原路返回给 LLM。MCP 的价值在于这条链路里的“工具描述”和“调用协议”被统一了换一个模型、换一个工具不用重写整套胶水代码。这篇文章适合谁如果你正在做 Agent、想让模型调用自己的 API、或者被各种工具接入格式搞烦了那这篇就是写给你的。我会用“小酌”式的轻松视角把架构拆开再给你能直接复制的服务端配置和客户端调用示例最后跑一次完整的工具调用验证。全程不绕弯能跟做。2. 动手前的前置准备TaoToken 接入与 MCP 运行环境在写 MCP Server 之前得先解决“模型从哪来”的问题。MCP 只是工具调用协议它不提供模型。你需要一个能调 Function Calling 的 LLM 接口。我这边用的是 TaoToken 的 API它兼容 OpenAI 的接口标准Function Calling 的请求格式可以直接套用省得自己再封装一层。先说清楚 TaoToken 是什么、能做什么。它是一个大模型 API 聚合服务提供统一的接口来调用多种模型支持对话、Function Calling、流式输出等能力。对做 MCP 的人来说最实用的点是它的接口和 OpenAI 兼容MCP Client 里配置 Base URL 和 Key 就能用不用为每个模型单独写适配。官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。接下来是环境准备。MCP 官方提供了 Python 和 TypeScript 的 SDK我选 Python因为写起来快、调试方便。你需要Python 3.10 以上建议 3.11。低于 3.10 有些类型语法会报错。 安装 MCP SDKpip install mcp。如果你要用 SSE 传输还需要pip install mcp[cli]或者单独装uvicorn、starlette这类 ASGI 依赖。 一个能发 HTTP 请求的客户端curl 或 Postman 都行我后面用 curl 演示。 TaoToken 的 API Key在控制台创建地址是 https://taotoken.net/api-keys 。关于模型选择Function Calling 对模型能力有要求不是所有模型都支持得一样好。实测下来支持工具调用的模型在参数填充上更稳。你在 TaoToken 控制台里能看到可用模型列表选一个标注支持 function calling 的即可。Model ID 要记下来后面配置里要填。这里有个容易踩的坑很多人以为 MCP Server 自己会去调 LLM其实不是。MCP Server 只负责暴露工具、执行工具它不碰模型。调模型的是 MCP Client 那一侧。所以你的 API Key 是配在 Client 里的不是 Server 里。这个分工一开始容易搞混记住“Server 管工具Client 管模型”就不会乱。环境变量建议这样组织方便后面复制export TAOTOKEN_API_KEY你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api export MCP_MODEL_ID你选的模型ID把 Key 放环境变量而不是硬编码是为了避免提交代码时泄露。这个习惯在接 MCP 的时候尤其重要因为 Client 配置里经常要写 Key。3. 可复制的 MCP 服务端配置与客户端调用示例这一节是核心我给你两份能直接跑的代码一个 MCP Server暴露一个查询天气的工具一个 MCP Client通过 TaoToken 调模型让模型决定是否调用这个工具。传输方式我先用 SSE因为这是远程调用的经典形态也是理解 HTTP 协同的关键。先看 MCP Server。它用官方 SDK 的 FastMCP 写法最简# weather_server.py from mcp.server.fastmcp import FastMCP mcp FastMCP(weather-server) mcp.tool() def get_weather(city: str) - str: 查询指定城市的天气。city 是城市名例如 北京。 fake_db { 北京: 晴18-26 摄氏度, 上海: 多云20-28 摄氏度, 深圳: 阵雨24-30 摄氏度, } return fake_db.get(city, f暂未收录 {city} 的天气数据) if __name__ __main__: mcp.run(transportsse)这段代码做了三件事创建一个名为 weather-server 的 MCP 服务用mcp.tool()装饰器把一个普通 Python 函数注册成工具函数的 docstring 会成为工具描述模型靠它判断什么时候调最后用 SSE 传输启动。启动命令python weather_server.py默认会监听本地端口SSE 的端点通常是/sse。你会在终端看到类似Uvicorn running on http://127.0.0.1:8000的输出。这就是 MCP Server 的 HTTP 入口。接下来是 MCP Client。它要做的事连接 Server、拉取工具列表、把工具列表转成 OpenAI 格式传给 TaoToken、解析模型的 Function Calling 输出、执行工具、把结果回传模型。完整示例如下# mcp_client.py import asyncio import json import os from openai import OpenAI from mcp import ClientSession from mcp.client.sse import sse_client TAOTOKEN_BASE_URL os.environ[TAOTOKEN_BASE_URL] TAOTOKEN_API_KEY os.environ[TAOTOKEN_API_KEY] MODEL_ID os.environ[MCP_MODEL_ID] client OpenAI(base_urlTAOTOKEN_BASE_URL, api_keyTAOTOKEN_API_KEY) def mcp_tools_to_openai(tools): return [ { type: function, function: { name: t.name, description: t.description or , parameters: t.inputSchema, }, } for t in tools ] async def main(): async with sse_client(http://127.0.0.1:8000/sse) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools_resp await session.list_tools() openai_tools mcp_tools_to_openai(tools_resp.tools) print(已加载工具:, [t[function][name] for t in openai_tools]) messages [{role: user, content: 帮我查一下北京现在的天气}] resp client.chat.completions.create( modelMODEL_ID, messagesmessages, toolsopenai_tools, tool_choiceauto, ) msg resp.choices[0].message messages.append(msg) if msg.tool_calls: for call in msg.tool_calls: args json.loads(call.function.arguments) print(模型决定调用:, call.function.name, args) result await session.call_tool(call.function.name, args) content result.content[0].text messages.append({ role: tool, tool_call_id: call.id, content: content, }) final client.chat.completions.create( modelMODEL_ID, messagesmessages, toolsopenai_tools ) print(最终回答:, final.choices[0].message.content) else: print(模型未调用工具:, msg.content) asyncio.run(main())这里有几个关键点值得展开。第一mcp_tools_to_openai这个转换函数是整条链路的枢纽MCP 的工具描述用的是 JSON SchemaOpenAI 的 Function Calling 也吃 JSON Schema所以inputSchema可以直接塞进parameters。这就是 MCP 和 Function Calling 能对接上的技术基础。第二tool_choiceauto让模型自己决定调不调。你也可以强制{type: function, function: {name: get_weather}}来测试。第三工具执行是通过session.call_tool走的这一步底层就是 MCP 的 JSON-RPC over SSE。Client 发一个请求Server 执行函数结果通过 SSE 流回来。如果你要用 Streamable HTTP 而不是 SSEServer 端把transportsse改成transportstreamable-httpClient 端把sse_client换成对应的streamablehttp_client端点路径也会变。这是 MCP 较新的传输方式更贴近标准 HTTP 语义。配置里三件套一定要对齐Base URL 是https://taotoken.net/apiKey 是你的 TaoToken KeyModel ID 是控制台里选的模型。这三个任何一个错后面都会报错第 5 节我会逐个拆。4. 跑一次完整验证从请求到工具返回的成功链路代码写完了现在跑一遍看整条链路是不是通的。这一步很重要因为 MCP 涉及模型、Client、Server 三方任何一环出问题都表现为“没反应”或“报错”必须分段验证。第一步启动 Server。开一个终端python weather_server.py看到 Uvicorn 启动日志后先别急着跑 Client用 curl 验证 SSE 端点活着curl -N http://127.0.0.1:8000/sse-N是关闭缓冲你会看到 SSE 流持续输出类似event: endpoint加一行data: /messages/?session_id...。这说明 Server 的 SSE 通道正常。按 CtrlC 退出 curlServer 继续跑。第二步跑 Client。另开一个终端确保环境变量已导出然后python mcp_client.py预期输出大概是这样已加载工具: [get_weather] 模型决定调用: get_weather {city: 北京} 最终回答: 北京现在是晴天气温 18 到 26 摄氏度。看到这三行说明整条链路通了。拆解一下发生了什么Client 通过 SSE 连上 Server调用list_tools拿到get_weather的描述Client 把工具转成 OpenAI 格式连同用户问题发给 TaoToken模型判断需要调工具返回tool_calls参数是{city: 北京}Client 解析参数通过session.call_tool把请求经 SSE 发给 ServerServer 执行函数返回天气字符串Client 把结果作为role: tool的消息追加再调一次模型模型基于工具结果生成自然语言回答。这里能直观看到 Function Calling 和 SSE/HTTP 的分工Function Calling 负责“模型决定调什么、参数是什么”这是模型侧的能力SSE/HTTP 负责“请求怎么传到 Server、结果怎么传回来”这是传输侧的事。MCP 把两者粘在一起定义了工具怎么描述、调用怎么发起、结果怎么回填。第三步验证多轮和边界。把用户问题改成“上海和深圳天气对比”模型可能会发起两个 tool_callsClient 的循环要能处理多个调用。再试一个不存在的城市比如“查一下火星的天气”Server 返回“暂未收录”模型会基于这个结果组织回答。这两个用例能验证你的 Client 循环写得够不够健壮。如果你用的是 Streamable HTTP验证方式类似只是 curl 的端点和请求头不同需要带Accept: text/event-stream。核心观察点一样工具列表能拉到、模型能决定调用、结果能回填。实测下来最容易出问题的不是代码逻辑而是环境变量没导出、Server 没启动、端口被占。所以每次跑之前先确认 Server 日志在滚再跑 Client。5. 本篇常见错误排查401、local proxy failed、reading choices 与 OAuth这一节我把接 MCP 时真实遇到过的报错列出来对照着查能省不少时间。每个报错我都给现象、原因、解法。401 Unauthorized。现象是 Client 调 TaoToken 时直接抛异常提示 invalid api key 或 unauthorized。原因基本是 Key 错了、没导出、或者复制时带了空格。解法先echo $TAOTOKEN_API_KEY确认环境变量有值再去 TaoToken 控制台重新生成一个 Key 对比。注意 Base URL 别写成带路径的https://taotoken.net/api/v1又叠加 SDK 自己的/v1导致路径重复。标准写法就是https://taotoken.net/apiSDK 会自己拼/v1/chat/completions。local proxy failed / connection refused。现象是 Client 连 SSE 端点时报连接失败。原因通常是 Server 没启动、端口不对、或者地址写错。解法确认python weather_server.py还在前台跑着curl -N http://127.0.0.1:8000/sse能出流。如果 Server 启动时报端口占用换个端口Client 里的 URL 同步改。还有一种情况是防火墙拦了本地回环这种少见但公司网络环境下可能遇到。reading choices of undefined。现象是resp.choices[0]报 undefined。原因一般是接口返回了错误结构比如返回体是{error: {...}}而不是标准的 chat completion。解法在client.chat.completions.create后面先打印resp看原始返回。常见触发点是 Model ID 写错模型不存在时接口返回错误对象。去 TaoToken 控制台核对 Model ID确保它支持 function calling。OAuth / 认证相关报错。MCP 协议目前在安全认证这块还在完善如果你接的是需要 OAuth 的远程 MCP Server可能会遇到 token 过期、scope 不足的提示。现象是 initialize 阶段就失败。解法确认 Server 端要求的认证方式Client 侧按文档带上对应的 header。本地开发阶段建议先用无认证的 Server 把链路跑通再上认证。工具列表为空。现象是list_tools返回空数组模型自然也不会调。原因通常是mcp.tool()装饰器没生效或者函数签名有问题。解法确认装饰器在函数定义正上方函数有类型注解和 docstring。MCP 靠 docstring 生成描述没有描述模型不知道这工具干嘛的。模型不调工具直接回答。现象是msg.tool_calls为空。原因可能是工具描述太模糊或者模型本身 function calling 能力弱。解法把 docstring 写具体参数说明清楚换一个 function calling 支持更好的模型测试时用tool_choice强制指定工具先验证执行链路再放开自动选择。排查顺序建议固定下来先 curl 验 Server再单独调一次 TaoToken 的 chat 接口验 Key 和模型最后跑完整 Client。分段隔离问题定位快很多。6. 把 MCP 用起来从验证到长期编码与 Agent 的落地路径链路跑通之后下一步就是把它用到真实场景里。MCP 的价值不在 demo而在于你能快速把已有 API 包装成工具让模型调用。我自己的做法是先把内部常用的几个查询接口用 MCP Server 包一层注册成工具然后在 Agent 里通过 Client 统一加载。这样换模型、加工具都不用改 Agent 主逻辑。如果你要长期做编码类或 Agent 类的工作建议关注 TaoToken 的 Coding Plan地址是 https://taotoken.net/coding-plan 。它适合需要持续调用模型、跑工具链的场景比单次按量更划算。模型对话调试可以用 https://taotoken.net/models 接入文档在 https://taotoken.net/doc API Key 管理在 https://taotoken.net/api-keys 。这几个入口按需取用。回到技术本身MCP 目前的传输选型确实有讨论空间。SSE 适合流式但把大量存量 HTTP API 迁过来成本不低Streamable HTTP 更贴近标准 HTTP是更务实的方向。Function Calling 作为模型侧能力短期内不会消失MCP 是在它之上做标准化封装。理解了这个分层你在设计自己的工具接入方案时就不会被协议细节带偏。最后给一个实用建议写 MCP Server 时工具粒度别太细也别太粗。太细会导致模型一次要选很多工具容易选错太粗会导致参数复杂模型填不对。一个工具对应一个明确的动作参数控制在三五个以内描述写清楚“什么时候用”模型的表现会稳很多。这个经验是我踩过几次坑之后总结的比协议本身更影响落地效果。
返回列表