资讯动态

MCP 协议实战:用 TaoToken 统一 Key 打通 AI Agent 的 JSON-RPC 调用链

发布时间:2026/10/8 21:54:44 来源:尧图企业网站定制
1. 从一次 JSON-RPC 报错说起MCP 协议到底解决什么问题如果你最近在折腾 AI Agent大概率见过这样的场景Agent 想调用一个本地工具查数据库代码里写死了函数名和参数格式换一个模型供应商工具描述又得重写一遍想让 Claude Desktop 和自研 Agent 共用同一套工具结果两边协议对不上。这些问题的根子在于模型和外部工具之间缺少一层统一的通信约定。MCPModel Context Protocol模型上下文协议就是冲着这个痛点来的。它基于 JSON-RPC 2.0 定义了一套标准消息格式把「模型想调用什么工具」和「工具怎么执行」解耦开。你可以把它理解成 AI 世界的 USB-C 接口Server 端负责暴露工具Tools、资源Resources和提示PromptsClient 端负责发现并调用这些能力中间走的是标准的 JSON-RPC 请求响应。这套协议适合谁我观察下来有三类人最需要一是做 AI Agent 编排的开发者需要让多个工具动态注册而不是硬编码二是想把本地能力文件系统、数据库、内部 API安全暴露给模型的团队三是希望在不同模型供应商之间自由切换、不被某家 Function Calling 格式绑死的工程师。MCP 的 JSON-RPC 通信层是纯文本、可调试的出问题时你能直接看到请求体和响应体这点比很多黑盒 SDK 友好得多。这篇内容聚焦 MCP 的通信层实战。我会用 Python 写一个最小的 MCP Server 和 Client把工具注册、请求路由、响应解析这条链路完整跑通然后把模型调用的 endpoint 切到 TaoToken 统一管理 Key最后用一次真实的工具调用验证整条链路。全程代码可复制报错可对照排查。2. TaoToken 前置准备统一 Key 与 endpoint 配置在动手写 MCP 代码之前先把模型调用的出口理清楚。MCP 本身只负责工具通信但 Agent 最终还是要调 LLM 来做意图判断和参数提取。如果每个项目都散落着不同的 API Key 和 base_url维护成本会很高。我的做法是用 TaoToken 做统一入口一个 Key 覆盖多个模型endpoint 也统一。TaoToken 的 API 地址是https://taotoken.net/api注意这个地址不带任何查询参数直接作为 base_url 使用。官网入口在https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content注册后在控制台生成 Key 即可。控制台地址是https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewriteAPI Keys 管理页在https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite。这里有个关键点MCP 的 JSON-RPC 通信和模型调用是两条独立的链路。JSON-RPC 走的是 stdio 或 HTTP模型调用走的是 OpenAI 兼容的/v1/chat/completions。很多人第一次配的时候会把两者混在一起导致 base_url 填错。正确的做法是MCP Server 的启动命令里不涉及模型 endpoint模型 endpoint 只在 Agent 主程序里配置。我建议用环境变量管理 Key避免硬编码。在项目根目录建一个.env文件TAOTOKEN_API_KEYsk-你的实际Key TAOTOKEN_BASE_URLhttps://taotoken.net/api然后在 Python 里用python-dotenv加载。这样 MCP Server 和 Client 都能读到同一份配置切换环境时只改.env就行。如果你用的是 Claude Code 这类工具它的配置方式略有不同需要走settings.json或环境变量注入后面第 3 节会给具体片段。有一点要提醒TaoToken 是合规的 API 聚合入口不是所谓的「中转」。它的作用是让你用一个 Key 调用多个模型省去分别申请和管理 Key 的麻烦。配置时确保 base_url 写对不要多加/v1后缀SDK 会自动拼接。3. 可复制配置server.py 与 client.py 完整片段这一节是核心我给出两个文件的完整代码。先装依赖pip install mcp openai python-dotenvmcp是官方 Python SDKopenai用来调 TaoToken 的兼容接口。先写server.py# server.py import asyncio import json from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent app Server(demo-mcp-server) # 模拟一个用户数据源 USERS [ {id: 1, name: 张三, role: 工程师}, {id: 2, name: 李四, role: 产品经理}, ] app.list_tools() async def list_tools() - list[Tool]: return [ Tool( nameget_user_by_id, description根据用户ID获取用户信息, inputSchema{ type: object, properties: { user_id: {type: integer, description: 用户ID} }, required: [user_id], }, ), Tool( namelist_users, description列出所有用户, inputSchema{type: object, properties: {}}, ), ] app.call_tool() async def call_tool(name: str, arguments: dict) - list[TextContent]: if name get_user_by_id: uid arguments.get(user_id) user next((u for u in USERS if u[id] uid), None) text json.dumps(user, ensure_asciiFalse) if user else 用户不存在 return [TextContent(typetext, texttext)] if name list_users: return [TextContent(typetext, textjson.dumps(USERS, ensure_asciiFalse))] return [TextContent(typetext, textf未知工具: {name})] async def main(): async with stdio_server() as (read, write): await app.run(read, write, app.create_initialization_options()) if __name__ __main__: asyncio.run(main())再写client.py它负责启动 Server 子进程、发 JSON-RPC 请求、解析响应# client.py import asyncio import json import os from dotenv import load_dotenv from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client from openai import OpenAI load_dotenv() # 模型调用走 TaoToken llm OpenAI( api_keyos.getenv(TAOTOKEN_API_KEY), base_urlos.getenv(TAOTOKEN_BASE_URL), ) async def run(): params StdioServerParameters(commandpython, args[server.py]) async with stdio_client(params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() # 1. 发现工具 tools await session.list_tools() print(可用工具:, [t.name for t in tools.tools]) # 2. 调用工具 result await session.call_tool(get_user_by_id, {user_id: 1}) print(工具返回:, result.content[0].text) # 3. 把工具结果交给模型做自然语言总结 resp llm.chat.completions.create( modelgpt-4o-mini, messages[ {role: system, content: 你是助手根据工具返回的JSON用中文回答。}, {role: user, content: f用户信息{result.content[0].text}}, ], ) print(模型总结:, resp.choices[0].message.content) if __name__ __main__: asyncio.run(run())如果你用 Claude Code配置片段放在~/.claude/settings.json或项目级.mcp.json{ mcpServers: { demo: { command: python, args: [server.py], env: { TAOTOKEN_API_KEY: sk-你的Key, TAOTOKEN_BASE_URL: https://taotoken.net/api } } } }三件套对照Base URL 填https://taotoken.net/apiKey 填控制台生成的sk-开头字符串Model ID 填gpt-4o-mini或你账号下可用的模型名。Cline 的 MCP 配置类似在cline_mcp_settings.json里按同样结构写。Codex 的auth.json则是另一套格式把 base_url 和 key 填进对应字段即可。4. 验证请求一次真实工具调用的完整链路配置写完后跑起来验证。先单独启动 Server 确认不报错python server.py如果卡住不动是正常的stdio 模式在等 Client 连接。按 CtrlC 退出然后跑 Clientpython client.py预期输出可用工具: [get_user_by_id, list_users] 工具返回: {id: 1, name: 张三, role: 工程师} 模型总结: 用户张三ID为1职位是工程师。这三行分别对应链路的三个阶段。第一行是tools/list的 JSON-RPC 响应证明工具注册成功第二行是tools/call的响应证明请求路由和参数传递正确第三行是模型调用成功证明 TaoToken 的 endpoint 配置无误。如果你想看底层 JSON-RPC 报文长什么样可以在 Client 里加日志。MCP 的请求体大致是{jsonrpc:2.0,id:1,method:tools/call,params:{name:get_user_by_id,arguments:{user_id:1}}}响应体{jsonrpc:2.0,id:1,result:{content:[{type:text,text:{\id\:1,...}}]}}看到这个结构你就明白了MCP 的通信层就是标准的 JSON-RPCmethod决定路由到哪个 handlerparams是参数result是返回。工具注册的本质是 Server 响应tools/list时返回一个 Tool 数组Client 拿到后可以动态展示给模型。实测下来整条链路从 Client 启动到模型返回大约 2-3 秒其中模型调用占大头。如果你只想验证 MCP 通信层可以先把模型调用那段注释掉只看前两行输出。5. 常见报错排查401、local proxy failed 与 reading choices这一节列几个我踩过的坑对照报错找原因。401 Unauthorized模型调用返回 401说明 TaoToken 的 Key 没读到或填错了。先检查.env是否被load_dotenv()正确加载再确认 Key 没有多余空格。如果是在 Claude Code 里报 401检查settings.json的env字段是否把 Key 传给了子进程。注意 base_url 不要写成https://taotoken.net/api/v1SDK 会自己拼/v1多写一层会 404 或 401。local proxy failed / connection refused这个报错通常出现在 stdio 模式下 Server 启动失败。检查command和args是否指向正确的 Python 解释器。如果你用虚拟环境command要写 venv 里的 python 绝对路径不能只写python。另外确认server.py路径是相对 Client 工作目录的路径不对会直接找不到文件。reading choices 报错 / KeyError choices说明模型返回的结构不是预期的 OpenAI 格式。常见原因是 base_url 指向了错误的 endpoint或者模型名写错导致返回了错误对象。打印resp原始内容看看如果是{error: ...}就说明请求没成功。确认 Model ID 是你账号下真实可用的不要凭记忆填。OAuth 相关报错如果你在 Claude Code 里看到 OAuth 字样通常是认证方式冲突。MCP Server 本身不走 OAuth走的是 stdio 或 HTTP。检查是不是把 MCP 配置和模型认证配置混在了同一个文件里。分开管理MCP 的mcpServers只管进程启动模型认证走环境变量或单独的 auth 配置。工具调用返回「未知工具」说明call_tool里的 name 匹配没命中。检查list_tools返回的 name 和call_tool里判断的字符串是否完全一致大小写敏感。另外确认 Client 传的arguments键名和inputSchema里定义的一致。排查时有个通用技巧在call_tool开头加一行print(f收到调用: {name}, 参数: {arguments})直接看 Server 端收到了什么。stdio 模式下 print 会输出到 stderr不影响 JSON-RPC 通道。6. 把链路用起来从最小示例到生产接入最小链路跑通后你可以按需扩展。工具注册这块把USERS换成真实的数据库查询或内部 API 调用即可inputSchema用 JSON Schema 描述清楚参数类型模型才能正确提取。请求路由方面工具多了以后建议按业务域拆分多个 ServerClient 端并行连接避免单个 Server 过于臃肿。模型调用这块TaoToken 的统一 Key 优势在多模型场景下会体现出来。比如意图识别用便宜的小模型复杂推理用大模型只改model参数base_url 和 Key 都不用动。如果你要长期跑编码类 Agent可以看看 Coding Plan 的额度方案地址是https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite。想先在线试模型效果的模型对话入口在https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite。接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite里面有各语言 SDK 的配置示例。Claude Code 的专项接入说明在https://taotoken.net/doc/claudecode?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite如果你用 Claude Code 做主力开发工具这份文档能省不少配置时间。最后说个实用技巧MCP Server 的调试不要一上来就接真实模型先用list_tools和call_tool把通信层验证通过再接模型。这样出问题时能快速定位是协议层还是模型层。我习惯在 Client 里加一个--dry-run参数只跑工具调用不调模型排查效率高很多。整条链路的核心就是 JSON-RPC 的请求响应把这层看透了上面接什么模型、下面挂什么工具都是可替换的零件。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑