第08章:MCP 模型上下文协议(下)
适合读者:已阅读 MCP 基础篇,理解 Host/Client/Server 三层架构,希望深入掌握工具 Schema 设计和 LangChain 集成实战的开发者。
阅读目标:读完本文,你将能独立设计复杂嵌套 Schema、理解 MCP→LangChain 适配器原理,并构建完整的 MCP Agent。
配套代码:/08_mcp/02_mcp_with_langchain.py(适配器、Agent 核心代码以及create_order嵌套参数示例均已包含在此文件中,可直接运行)
前期回顾
- 第01章:从零开始调用 LLM-入门 Qwen 大模型 API
- 第02章:Prompt 工程 —— 用语言精准指挥 AI
- 第03章:LCEL 链式调用——让 AI 任务像流水线一样运转
- 第04章:AI Tools 工具调用 —— 让 AI 伸出"手"与世界交互
- 第05章:AI Agent 智能体 —— 让 AI 自主决策,循环解决问题
- 第06章:AI RAG 检索增强生成 — 从零到生产(上)
- 第06章:AI RAG 向量库选型与生产实践
- 第07章(上):LangGraph 工作流 —— 为什么需要它,以及如何入门
- 第07章(下):LangGraph 工作流进阶 —— 检查点、人工介入与多 Agent 协作
- 第08章:MCP 模型上下文协议(上)
0. 前置知识快速回顾
在基础篇中,我们掌握了:MCP 是 AI 工具领域的"USB 接口"标准(JSON-RPC 2.0 通信),解决了 N×M 适配问题;核心架构是 Host/Client/Server 三层,提供 Tools/Resources/Prompts 三大组件;工具用 JSON Schema 描述接口,服务器注册工具并处理调用请求。
本文聚焦进阶实战,重点覆盖以下三块:
- 🔑工具 Schema 设计:从简单参数到深度嵌套对象/数组(以电商订单为例)
- 🔑适配器原理:
json_schema_to_pydantic和mcp_tool_to_langchain逐行解析 - 🔑完整 Agent 构建:消息循环、工具路由、ToolMessage 反馈机制
1. 工具参数 Schema 从简单到复杂
MCP 工具的inputSchema遵循 JSON Schema 标准。理解如何从简单到复杂地设计 Schema,是开发高质量 MCP 工具的基础。
1.1 简单参数:基础类型
最常见的场景:工具只需要几个基础类型参数(字符串、整数、布尔值)。
来看get_weather工具的定义:
{"name":"get_weather","description":"获取指定城市的当前天气信息","inputSchema":{"type":"object","properties":{"city":{"type":"string","description":"城市名称,如北京、上海、广州",},"unit":{"type":"string","description":"温度单位:celsius(摄氏度)或 fahrenheit(华氏度)","enum":["celsius","fahrenheit"],"default":"celsius",},},"required":["city"],# ← 只有 city 是必填},}几个关键点:
| 字段 | 作用 |
|---|---|
type: "string" | 参数是字符串类型 |
description | LLM 通过这段描述理解参数含义,写清楚非常重要 |
enum | 限定参数的合法取值范围,防止 LLM 乱填 |
default | 可选参数的默认值 |
required | 列表中没有的字段是可选的 |
对应的处理函数签名与 Schema 完全对应:
def_get_weather_impl(city:str,unit:str="celsius")->str:weather_data={"北京":{"condition":"晴天","temp":22,"humidity":45},"上海":{"condition":"多云","temp":26,"humidity":70},"广州":{"condition":"小雨","temp":28,"humidity":85},"成都":{"condition":"阴天","temp":20,"humidity":75},}forkey,datainweather_data.items():ifkeyincityorcityinkey:temp=data["temp"]ifunit=="fahrenheit":temp=temp*9/5+32unit_str="°F"else:unit_str="°C"returnf"{key}:{data['condition']},{temp}{unit_str}, 湿度{data['humidity']}%"returnf"未找到{city}的天气数据"📌规律:
required中的字段 → Python 函数必填参数;不在required中的字段 → Python 函数有默认值的参数。
1.2 中等复杂:可选参数与默认值
search_news展示了带有默认值的可选整数参数:
{"name":"search_news","description":"搜索最新的新闻和资讯","inputSchema":{"type":"object","properties":{"query":{"type":"string","description":"搜索关键词",},"limit":{"type":"integer","description":"返回条数,默认3","default":3,# ← Schema 中声明默认值},},"required":["query"],# ← limit 不在 required 里,是可选参数},}对应实现函数:
def_search_news_impl(query:str,limit:int=3)->str:news_db={"人工智能":["GPT-5 预计将在2025年发布,性能大幅提升","国内大模型竞争加剧,百炼、文心、豆包激烈角逐","AI 辅助编程工具日益普及,提升开发效率50%",],"python":["Python 3.13 正式发布,引入自由线程模式","Python 连续第5年成为最受欢迎编程语言",],}results=[]query_lower=query.lower()forkey,news_listinnews_db.items():ifkeyinquery_lowerorquery_lowerinkey:results.extend(news_list[:limit])breakifnotresults:results=[f"关于'{query}'的最新资讯暂无数据"]return"\n".join(f"{i+1}.{news}"fori,newsinenumerate