首页 / 资讯中心 / 文章详情

大语言模型实战(十七)——MCP语法全解:从Server到Client的Model Context Protocol实战手册

大语言模型实战(十七)——MCP语法全解:从Server到Client的Model Context Protocol实战手册 ★ FEATURED ARTICLE
1. 从一次本地 MCP 调用失败说起Model Context Protocol 语法链路到底卡在哪如果你正在搜 MCP、Model Context Protocol、MCP 语法、MCP Server、MCP Client 这些关键词大概率已经看过协议介绍却在真正跑通一次本地调用时卡住。我最初接触 MCP 时Server 端装饰器写好了Client 端也连上了但list_tools()返回空列表call_tool()直接抛异常日志里只有一行local proxy failed。问题不在协议本身而在于 Server 端和 Client 端的语法链路没有对齐Server 注册了什么方法、返回什么类型、Client 用什么参数调用、初始化握手有没有完成每一步都有固定语法。MCP 全称 Model Context Protocol直译是模型上下文协议。它做的事情可以用一句话概括把 AI 应用和外部数据、工具之间的通信方式标准化。你可以把它理解成 AI 世界里的 USB-C 接口——Server 是提供数据或工具的一方Client 是消费数据或工具的一方中间用 JSON-RPC 2.0 传递消息底层走 stdio 或 HTTP。适合谁适合想把本地文件、数据库、内部 API 接给大模型用的开发者也适合想给 Claude Code、Cline 这类工具写自定义能力的同学。这篇内容聚焦从 Server 到 Client 的完整语法链路交付可复制的配置片段和逐条验证动作。我会先讲清楚 MCP 的四大功能语法再给出 Server 端和 Client 端的可运行代码接着用 TaoToken 作为模型侧接入点跑通一次完整调用最后把常见报错逐条对照排查。你跟着做能在本地跑通一次完整的 MCP 调用流程。MCP 的四大功能分别是 Resources、Tools、Prompts、Sampling。Resources 提供被动数据比如文档、配置、文件Tools 执行主动操作比如 API 调用、计算、搜索Prompts 提供提示词模板Sampling 请求 LLM 采样。Server 端用装饰器注册这些能力Client 端用对应方法调用。语法链路的核心就是Server 注册的方法名、参数类型、返回类型必须和 Client 调用的方法名、参数结构、解析方式一一对应。任何一处不匹配调用就会失败。2. TaoToken 前置准备MCP Client 接入模型侧的 Base URL 与 Key 配置MCP 本身只负责 Server 和 Client 之间的通信真正生成内容的是 LLM。所以跑通 MCP 调用除了 Server 和 Client还需要一个模型接入点。我用 TaoToken 作为模型侧的统一入口它兼容 OpenAI 风格的接口Client 端拿到 Prompt 后可以直接把 messages 转发过去。先做前置准备。打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册账号然后进入控制台创建 API Key。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite API Keys 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。创建好 Key 之后模型侧需要三个东西Base URL、API Key、Model ID。Base URL 用 https://taotoken.net/api 注意这个地址不加 UTM 参数。API Key 就是你刚创建的那串。Model ID 根据你要用的模型填比如 claude-sonnet-4-20250514、gpt-4o 这类。如果你不确定用哪个模型可以先到模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 试一下确认模型能正常返回再写进代码。这里要强调一个容易踩的坑MCP Client 和模型接入点是两个独立配置。MCP Client 负责和 Server 通信模型接入点负责生成内容。很多人把两者混在一起以为配了 MCP Server 就不需要模型 Key 了结果 Prompt 拿到了却没人生成回答。正确的链路是Client 通过 MCP 协议从 Server 拿到 Resources、Tools、Prompts然后把 Prompt 内容转发给模型接入点模型返回结果Client 再决定是否把结果写回 Server。如果你打算长期做编码类或 Agent 类任务可以看一下 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它更适合高频调用场景。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的接口说明。Claude Code 相关的接入说明在 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecode-anthropicutm_campaignrewrite 。配置模型侧的时候我建议先用一个最小请求验证 Key 是否可用。用 curl 发一个 chat completions 请求确认返回正常再进入 MCP 的代码环节。这样能把模型侧的问题和 MCP 侧的问题分开排查省很多时间。3. 可复制配置MCP Server 与 Client 的 JSON/TOML 片段及三件套这一节给出可直接复制的配置片段。先明确三件套Base URL、API Key、Model ID。无论你用哪种客户端这三个值都要填对。如果你用的是 Cline 或类似支持 MCP 的编辑器插件MCP Server 配置通常写在 settings JSON 里。下面是一个 stdio 类型的 Server 配置片段路径按你的实际项目改{ mcpServers: { my-local-server: { command: python, args: [/Users/yourname/projects/mcp-demo/server.py], env: { PYTHONUNBUFFERED: 1 } } } }这段配置的意思是Client 启动时用python命令执行server.py通过 stdio 建立通道。env里可以传环境变量比如你的 API Key 如果 Server 端也要用可以放这里。注意command和args必须指向真实存在的解释器和脚本路径写错会直接报spawn ENOENT。如果你用的是 Codex 风格的配置模型侧三件套写在auth.json或对应的配置文件里。下面是一个模型接入的配置片段{ base_url: https://taotoken.net/api, api_key: sk-你的Key, model: claude-sonnet-4-20250514 }Base URL 固定用 https://taotoken.net/api 不要加 UTM。API Key 从控制台复制。Model ID 按你实际要用的模型填。这三个值在 Client 端调用模型时使用。如果你用 TOML 格式管理配置比如某些 CLI 工具可以这样写[mcp.server.my-local-server] command python args [/Users/yourname/projects/mcp-demo/server.py] [llm.provider] base_url https://taotoken.net/api api_key sk-你的Key model claude-sonnet-4-20250514Server 端的代码配置也要对齐。下面是一个最小 Server 片段注册一个 Toolfrom mcp.server import Server import mcp.types as types from mcp.server.stdio import stdio_server import asyncio app Server(demo-server) app.list_tools() async def list_tools() - list[types.Tool]: return [ types.Tool( nameadd, description两数相加, inputSchema{ type: object, properties: { a: {type: number}, b: {type: number} }, required: [a, b] } ) ] app.call_tool() async def call_tool(name: str, arguments: dict) - list[types.TextContent]: if name add: result arguments[a] arguments[b] return [types.TextContent(typetext, textf结果: {result})] return [types.TextContent(typetext, text未知工具)] async def main(): async with stdio_server() as (read_stream, write_stream): await app.run(read_stream, write_stream, app.create_initialization_options()) if __name__ __main__: asyncio.run(main())Client 端配置片段import sys import asyncio from mcp import ClientSession from mcp.client.stdio import stdio_client, StdioServerParameters async def main(): params StdioServerParameters( commandsys.executable, args[server.py], envNone ) async with stdio_client(params) as (reader, writer): async with ClientSession(reader, writer) as session: await session.initialize() tools await session.list_tools() print(可用工具:, [t.name for t in tools.tools]) result await session.call_tool( nameadd, arguments{a: 5, b: 3} ) print(调用结果:, result.content[0].text) if __name__ __main__: asyncio.run(main())这三段配置覆盖了 Server 注册、Client 连接、模型接入三件套。复制之后按你的路径和 Key 改就能进入验证环节。4. 验证请求与成功结果逐条语法验证动作与返回解析配置写好后按顺序验证。第一步单独启动 Server确认不报错python server.py如果 Server 正常它会阻塞等待 stdio 输入没有输出是正常的。如果报ModuleNotFoundError: No module named mcp先装依赖pip install mcp第二步运行 Client观察输出。成功的话你会看到可用工具: [add] 调用结果: 结果: 8这两行说明 Server 注册的 Tool 被 Client 发现并且调用成功返回。如果可用工具是空列表说明list_tools()装饰器没生效检查函数名和返回类型。如果调用结果报错检查call_tool里的参数名是否和inputSchema一致。第三步验证 Resources。在 Server 端加一个list_resources和read_resourceClient 端调用resources await session.list_resources() for r in resources.resources: print(f资源: {r.name}, URI: {r.uri}) content await session.read_resource(r.uri) print(f内容: {content})成功返回会打印资源名和内容。注意read_resource的返回值是字符串不是对象直接打印即可。第四步验证 Prompts。Server 端注册list_prompts和get_promptClient 端调用prompts await session.list_prompts() for p in prompts.prompts: print(f模板: {p.name}, 参数: {[a.name for a in p.arguments]}) result await session.get_prompt( namecode-review, arguments{code: def hello(): pass, language: Python} ) for msg in result.messages: print(f角色: {msg.role}, 内容: {msg.content.text})成功返回会打印模板名、参数列表以及生成的消息内容。get_prompt返回的是GetPromptResult里面messages是列表每条消息有role和content。第五步把 Prompt 转发给模型接入点。拿到messages后转成 OpenAI 格式from openai import OpenAI llm OpenAI( base_urlhttps://taotoken.net/api, api_keysk-你的Key ) messages [ {role: msg.role, content: msg.content.text} for msg in result.messages ] response llm.chat.completions.create( modelclaude-sonnet-4-20250514, messagesmessages ) print(response.choices[0].message.content)成功返回会打印模型生成的审查意见。到这里一次完整的 MCP 调用流程就跑通了Client 通过 MCP 从 Server 拿到 Prompt转发给模型接入点模型返回结果。验证过程中有几个细节要注意。session.initialize()必须调用否则后续方法会报未初始化。call_tool的arguments必须是 dict键名和inputSchema里的properties一致。read_resource的 URI 要和list_resources返回的 URI 完全匹配包括file://前缀。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth 逐条对照这一节把常见报错逐条对照。第一个401 Unauthorized。这个通常出现在模型接入点不是 MCP 本身。检查 API Key 是否复制完整Base URL 是否是 https://taotoken.net/api 有没有多空格。如果 Key 没问题检查请求头格式OpenAI 风格是Authorization: Bearer sk-xxx。第二个local proxy failed。这个报错通常出现在 Client 启动 Server 进程时。原因可能是command路径不对或者args里的脚本不存在。检查StdioServerParameters里的command和args用绝对路径更稳。如果 Server 脚本有语法错误进程启动后立刻退出也会报这个。先单独运行python server.py确认能启动。第三个reading choices 相关报错。这个出现在解析模型返回时通常是返回结构不符合预期。检查response.choices[0].message.content是否存在。如果模型返回的是流式需要先聚合。如果返回体里没有choices说明请求本身失败了先看 HTTP 状态码。第四个OAuth 相关报错。如果你用的是需要 OAuth 的客户端检查 token 是否过期。MCP 本身不涉及 OAuth但某些客户端在连接远程 Server 时会用。本地 stdio 模式不需要 OAuth如果报这个检查是不是误配了远程地址。第五个装饰器名称错误。app.list_tool()少了个 s正确是app.list_tools()。这种错误不会报语法错但方法不会注册Client 端list_tools()返回空。对照检查所有装饰器名称list_resources、read_resource、list_tools、call_tool、list_prompts、get_prompt。第六个返回类型错误。call_tool必须返回list[types.TextContent]不能直接返回字符串。read_resource返回字符串。get_prompt返回types.GetPromptResult。类型不对会在 Client 端解析时报错。第七个JSON Schema 格式错误。inputSchema里properties的每个字段必须是对象比如{type: number}不能写成number。required是数组列出必填字段名。第八个初始化未完成。Client 端session.initialize()必须在调用其他方法前 await。如果跳过后续方法会报未初始化或超时。排查顺序建议先确认 Server 能单独启动再确认 Client 能连上并list_tools有返回再确认call_tool能执行最后确认模型接入点能返回。每一步单独验证不要跳步。6. 语义一致 CTA把 MCP 语法链路接到你的实际项目跑通一次本地调用之后下一步是把它接到实际项目。MCP 的价值在于标准化你可以把本地文件、数据库、内部 API 都封装成 Server让 Client 统一调用。模型侧继续用 TaoToken 作为接入点Base URL 固定 https://taotoken.net/api Key 从控制台管理。如果你在排障或接入阶段建议先看接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的接口说明和示例。API Keys 管理在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 需要新建或轮换 Key 时用。想先验证模型是否正常可以到模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 发一条消息试试。如果你打算长期做编码类或 Agent 类任务Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 更适合高频调用场景。Claude Code 相关的接入说明在 https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecode-anthropicutm_campaignrewrite 。最后给一个实用技巧把 Server 端的每个装饰器方法都加一行日志打印方法名和参数。Client 端每次调用前也打印请求内容。这样出问题时你能快速定位是 Server 没注册、Client 没调用还是模型侧没返回。MCP 的语法链路不复杂复杂的是每一步都要对齐。对齐了调用就通了。
阅读完成 · 觉得有帮助?
咨询建站