ARTICLE DETAIL

资讯详情

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

MCP协议技术解析与Python代码实现:基于JSON-RPC 2.0的双向转换实践与TaoToken配置

MCP协议技术解析与Python代码实现:基于JSON-RPC 2.0的双向转换实践与TaoToken配置 1. 从一次本地工具接入失败说起MCP协议全称 Model Context Protocol是一套让 AI 模型与本地工具、数据源之间用统一格式对话的约定。它能做什么简单说你写一个 Python 函数加上装饰器AI 就能通过 JSON-RPC 2.0 消息调用它拿到结构化结果后再转成自然语言回复。适合谁适合需要把本地文件系统、数据库、内部 API 接入 AI 工作流的开发者尤其是已经在用 LangChain、LangGraph 或 Claude Code 这类工具链的人。我最初接触 MCP 是因为一个很具体的需求让 AI 助手帮我查本地下载文件夹里的 PDF 文件而不是每次手动打开文件管理器。听起来简单但真动手时踩了不少坑。第一个坑是协议理解偏差——我以为 MCP 就是普通的 HTTP 接口结果发现它默认走 stdio 传输消息格式是 JSON-RPC 2.0请求和响应必须严格对齐 id 和 method。第二个坑是双向转换服务端返回的结构化数据客户端要转成自然语言中间涉及工具描述、参数 Schema、结果序列化三层转换任何一层对不上AI 就调用失败。更麻烦的是接入环节。本地跑通 stdio 模式后我想把服务端接到远程模型通道做验证结果发现 Key 管理、API 地址、模型路由三件事分散在不同地方调试成本很高。后来我用 TaoToken 的统一 Key 和 API 通道把这块收拢才把精力放回协议本身。这篇就按我实际跑通的顺序从服务端骨架、客户端集成、配置示例到验证请求一步步拆开讲代码可以直接复制。2. TaoToken 前置统一 Key 与 API 通道准备在写 MCP 代码之前先把模型通道准备好。MCP 服务端本身不依赖外部模型但客户端做自然语言转换时需要调用 LLM。我试过把 Key 硬编码在脚本里换环境就得改代码后来改成从环境变量读取配合 TaoToken 的统一通道切换模型时只改一个配置项。你需要先拿到一个可用的 API Key。访问 TaoToken 控制台创建 Key地址是 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。创建后复制保存后面配置里会用到。API 基础地址用 https://taotoken.net/api 注意这个地址不加 UTM 参数直接作为 base_url 填入客户端配置。如果你用的是 OpenAI 兼容的 SDK把 base_url 指向它即可如果用 LangChain 的 ChatOpenAI同样传这个地址。模型选择上验证阶段建议先用一个响应快的模型跑通链路确认 JSON-RPC 消息能正确往返后再换成你实际业务用的模型。TaoToken 的模型对话入口在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 可以先在网页里试一下模型是否正常响应排除 Key 或通道问题。注意Key 不要写进代码提交到仓库。用环境变量或本地 .env 文件管理.env 记得加进 .gitignore。3. 可复制配置MCP 服务端与客户端骨架3.1 服务端自然语言到结构化数据的转换先装依赖。我用的 Python 3.11MCP SDK 通过 pip 安装pip install mcp langchain-mcp-adapters langgraph langchain-openai python-dotenv服务端代码保存为 server.py。核心是用 FastMCP 注册工具装饰器会自动把函数签名和 docstring 转成 JSON Schema供客户端发现能力import os from mcp.server.fastmcp import FastMCP mcp FastMCP(FileSystemServer) mcp.tool() def list_directory(path: str) - list: 获取指定路径下的文件列表 Args: path: 需要查询的目录路径如 ~/Downloads Returns: 包含文件名和类型的结构化数据 full_path os.path.expanduser(path) if not os.path.isdir(full_path): return [{name: 路径不存在, type: error}] return [ {name: f, type: dir if os.path.isdir(os.path.join(full_path, f)) else file} for f in os.listdir(full_path) ] if __name__ __main__: mcp.run(transportstdio)这段代码跑起来后服务端会监听标准输入输出。当客户端发来 JSON-RPC 请求时SDK 自动路由到对应函数返回值序列化成响应结构。比如调用 list_directory 传 ~/Downloads返回的 JSON-RPC 响应大致是{ jsonrpc: 2.0, result: [ {name: report.pdf, type: file}, {name: photos, type: dir} ], id: 1 }3.2 客户端结构化数据到自然语言的转换客户端代码保存为 client.py。这里用 LangChain 的 MCP 适配器加载工具再交给 LangGraph 的 ReAct Agent 做自然语言转换import asyncio import os from dotenv import load_dotenv from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client from langchain_mcp_adapters.tools import load_mcp_tools from langgraph.prebuilt import create_react_agent from langchain_openai import ChatOpenAI load_dotenv() async def query_filesystem(): server_params StdioServerParameters( commandpython, args[server.py] ) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools await load_mcp_tools(session) llm ChatOpenAI( modelgpt-4o-mini, base_urlhttps://taotoken.net/api, api_keyos.getenv(TAOTOKEN_API_KEY) ) agent create_react_agent(llm, tools) response await agent.ainvoke({ messages: 请帮我查看下载文件夹里有哪些PDF文件 }) print(response[messages][-1].content) if __name__ __main__: asyncio.run(query_filesystem())核心流程分三步客户端通过 list_tools 拿到服务端能力描述大模型解析自然语言生成 JSON-RPC 请求method 是 list_directoryparams 里带 path服务端返回结构化数据后LangChain 通过模板生成自然语言响应。3.3 settings.json 与 config.toml 示例如果你用 Claude Code 或类似工具接入 MCP 服务端通常需要一份配置文件。settings.json 示例{ mcpServers: { filesystem: { command: python, args: [/absolute/path/to/server.py], env: { TAOTOKEN_API_KEY: your_key_here } } } }config.toml 示例适合用 uv 管理依赖的场景[project] name mcp-filesystem version 0.1.0 requires-python 3.11 dependencies [ mcp, langchain-mcp-adapters, langgraph, langchain-openai, python-dotenv ] [mcp.servers.filesystem] command uv args [run, server.py] transport stdio提示路径一定用绝对路径相对路径在 stdio 模式下容易因为工作目录不同而找不到文件。4. 验证请求跑通双向转换链路配置写完后先单独验证服务端。开一个终端跑python server.py如果没报错说明服务端在等待 stdio 输入。再开另一个终端跑客户端python client.py预期输出类似下载文件夹包含 3 个 PDF 文件report.pdf、manual.pdf、invoice.pdf。如果看到自然语言结果说明 JSON-RPC 双向转换链路已经跑通。想更细粒度地看协议消息可以用 MCP Inspector 调试npx modelcontextprotocol/inspector python server.py在交互界面里执行 list_tools能看到服务端注册的工具列表和参数 Schema再执行 call list_directory ~/Desktop直接观察 JSON-RPC 请求和响应原文。这一步对排查参数类型不匹配特别有用。验证模型通道是否正常可以单独发一个请求curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d {model:gpt-4o-mini,messages:[{role:user,content:ping}]}返回里有 choices 字段就说明 Key 和通道没问题。如果这里失败先解决通道问题再回头看 MCP 代码。5. 本篇常见错排查5.1 服务端启动即退出最常见的原因是 transport 参数写错。MCP SDK 默认可能是 sse 或其他模式必须显式写 transportstdio。另外检查 Python 版本低于 3.10 时部分异步语法会报错。5.2 客户端报 Tool not found说明 list_tools 没拿到服务端工具。先确认 server.py 里的 mcp.tool() 装饰器没漏再检查客户端 StdioServerParameters 的 args 路径是否正确。如果服务端有启动报错stdio_client 会静默失败建议先在终端手动跑一遍 server.py 看输出。5.3 JSON-RPC id 不匹配导致超时MCP 的请求和响应靠 id 关联。如果你自己手写 JSON-RPC 消息id 类型要一致别一个用数字一个用字符串。用 SDK 时一般不会遇到但自定义传输层时容易踩。5.4 模型返回乱码或空结果先确认 base_url 和 api_key 正确。TaoToken 的 API 地址是 https://taotoken.net/api 不要多加路径后缀。如果用的是 LangChainChatOpenAI 的 base_url 参数直接传这个地址SDK 会自动拼接 /v1/chat/completions。5.5 权限校验失败如果你在服务端加了 before_request 钩子做 Key 校验注意 stdio 模式下没有 HTTP headerscontext.headers 可能为空。这种场景建议把校验逻辑放在工具函数内部或者改用环境变量传递凭证。6. 接入文档与后续动作链路跑通后下一步通常是把 MCP 服务端接到长期运行的编码或 Agent 工作流里。如果你需要稳定的模型通道支撑多轮工具调用可以看 TaoToken 的 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 它适合需要持续调用模型做代码生成和工具编排的场景。接入细节和参数说明以官方文档为准https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。如果你用 Claude Code 接入 Anthropic 风格通道参考 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。最后留一个我踩过的坑MCP 服务端的工具函数返回值尽量保持结构简单嵌套太深时 LangChain 的模板转换容易丢字段。我一开始返回了带 metadata 的嵌套对象结果自然语言输出里只显示了文件名类型信息被吞了。改成扁平结构后转换就稳定了。
返回列表