在真实业务里,Agent 最大的痛点往往不是“模型不够聪明”,而是工具接得太痛苦。OpenAI Function Calling 刚出来时大家都觉得方便,可真要在一个项目里接五六个外部系统,函数定义、参数校验、鉴权、错误处理全都堆在业务代码里,越写越重。MCP 的出现在很大程度上改变了这个局面,它给“大模型调用工具”这件事提供了一套统一协议,而 LangChain 作为最常用的 Agent 编排框架,也很快补齐了对 MCP 的原生支持。这篇文章会从概念讲起,一步步带你把 MCP Server 搭起来,再通过 LangChain 把它加载成 Agent 工具,最终跑通调用链路。文章会覆盖 LangGraph 与 LangChain 的关系、DeepSeek API 接入、Claude Code 配置 MCP 等内容,适合正在做 AI Agent 开发、想统一工具接入方式的开发者阅读。
1. 背景与核心概念
1.1 从“大模型生成文本”到“大模型动手干活”
先回顾一下 Agent 进化的路径。最初我们使用大模型只是做文本生成、摘要、翻译,模型和外部世界没有任何交互。后来出现了 Function Calling,模型可以根据用户问题输出一个结构化的工具调用指令,再由代码去执行真实函数。再往后,LangChain 等框架把这些调用逻辑包装成 Agent,让模型能够在一次对话中多次决策:是否需要调用工具、调用哪个工具、拿到结果之后如何继续推理。
但这套流程有一个很现实的问题:不同框架、不同模型、不同工具库,Function Calling 的格式都不一样。你写了一个 Python 函数,想在 Claude Code 里用,又想在另一个 Agent 框架里用,就得分别适配。每接一个新的外部系统,都要重新做一遍胶水代码,而且这些代码往往没有复用价值。MCP 就是在这个背景下产生的。
1.2 什么是 MCP
MCP(Model Context Protocol,模型上下文协议)是 Anthropic 在 2024 年底提出的一种开放协议,目标是为大模型应用提供一套标准化的“工具接入”方式。它的核心设计思路是把“能力提供方”和“能力消费方”解耦。
你可以把 MCP 理解为 AI 应用世界的 USB-C 接口。一个 MCP Server 对外暴露工具、资源、提示词三种能力,任何支持 MCP 的客户端(Host)都可以通过标准协议去发现并调用这些能力,而不需要关心对方是用什么语言写的、部署在哪台机器上。目前主流的 Agent 客户端,比如 Claude Desktop、Claude Code、LangChain、Cursor 等,都已经支持 MCP 协议。
1.3 LangChain 和 LangGraph 分别是什么
很多新手容易把 LangChain 和 LangGraph 搞混,这里做个梳理。
LangChain 是一个生态型的开发框架,提供大模型调用、提示词管理、文档加载、向量检索、工具调用、Agent 编排等模块。它的主要价值是让开发者不用重复造轮子,把常用能力都封装好了。
LangGraph 是 LangChain 团队推出的一个低层编排框架,它把 Agent 的执行流程建模成一张图,节点(Node)表示计算单元,边(Edge)表示流转逻辑。相比 LangChain 早期版本的 AgentExecutor,LangGraph 提供了更好的可控性、可观测性和状态管理能力,适合生产级 Agent 开发。
在 LangGraph 中有一个非常实用的预置组件叫 create_react_agent,它用 ReAct 范式实现了一个完整的 Agent 循环:模型接收输入、判断是否调用工具、执行工具、返回结果、再交给模型推理。本文的实战部分就会基于这个组件来实现。
1.4 为什么需要 MCP + LangChain
LangChain 支持 Agent 工具已经不是新鲜事,关键是如何把 MCP 生态中的工具加载进来。通过 langchain-mcp-adapters,LangChain 可以直接加载远程或本地的 MCP Server 暴露的工具,自动把 MCP 工具转换为 LangChain 的 Tool 对象,然后交给 Agent 调用。
这样一来,工具侧只需要开发一次 MCP Server,就能同时服务于 LangChain Agent、Claude Code、Claude Desktop 等多个客户端。这也是为什么 MCP 被很多人视为 Agent 时代的“标准基础设施”。
本文的完整实战链路如下图:
用户提问 ↓ LangChain Agent(create_react_agent) ↓ 模型决定调用工具 langchain-mcp-adapters ↓ 通过 MCP 协议通信 MCP Server(FastMCP) ↓ 执行真实逻辑 外部系统 / 业务函数 / 数据库2. 环境准备与版本说明
2.1 基础环境
本文的代码以 Python 3.10 及以上版本为例,操作系统没有特殊限制,Windows、macOS、Linux 均可。建议新建一个独立的 Python 虚拟环境,避免把依赖装到全局环境里造成冲突。
python -m venv .venv source .venv/bin/activate # Windows 下使用 .venv\Scripts\activate2.2 安装依赖
需要安装的核心依赖如下:
pip install langchain langchain-openai langgraph langchain-mcp-adapters mcp fastmcp各包的作用如下:
| 依赖包 | 用途 |
|---|---|
| langchain | LangChain 核心库 |
| langchain-openai | 通过 OpenAI 兼容接口调用模型(DeepSeek 也走这个方式) |
| langgraph | Agent 编排框架,提供 create_react_agent |
| langchain-mcp-adapters | 将 MCP 工具加载为 LangChain Tool 的适配层 |
| mcp | MCP 官方 Python SDK |
| fastmcp | 快速开发 MCP Server 的高层框架 |
这里要特别提醒:MCP 协议和相关 SDK 更新速度很快,不同版本之间 API 可能会有差异。上面命令安装的是当前 PyPI 上的最新稳定版本。如果你在运行时遇到模块找不到或参数不兼容的问题,优先检查各包的版本,并根据项目实际情况调整。
2.3 项目结构
为了演示方便,本文使用下面的项目结构:
langchain-mcp-demo/ ├── .venv/ # 虚拟环境 ├── mcp_server.py # MCP Server 定义 ├── agent_client.py # LangChain Agent 客户端 ├── deepseek_agent.py # DeepSeek + MCP 示例 └── requirements.txt # 依赖清单3. 核心原理拆解
3.1 Agent 调用工具的本质
一个 Agent 调用工具,本质上是在“模型推理”和“外部执行”之间循环。以 ReAct 范式为例,完整的循环是:
- 用户输入问题。
- 模型根据当前对话内容和可用工具的描述,决定下一步动作。
- 如果模型认为需要调用工具,就输出一个结构化动作,包含工具名和参数。
- 框架执行对应工具,并把执行结果返回给模型。
- 模型根据工具结果继续推理,可能再次调用工具,也可能直接输出最终答案。
在这个过程中,模型的“工具感知”完全依赖工具的描述信息。工具名、参数说明、返回值格式写得越清晰,模型就越不容易调用错。这也是为什么在 MCP Server 里,tool 的 docstring 和参数注解非常重要。
3.2 MCP 协议的核心模型
MCP 采用客户端—服务器架构,涉及三个角色:
- Host:宿主程序,通常就是用户正在使用的 AI 应用,比如 Claude Desktop、Claude Code,或者你自己开发的 Agent 程序。
- Client:Host 内部的连接组件,负责与 Server 建立会话、发起请求。
- Server:能力提供方,暴露工具、资源、提示词。
通信层支持两种传输方式:
- stdio:Server 作为子进程启动,和客户端之间通过标准输入输出通信,适合本地开发。
- Streamable HTTP:Server 作为一个 HTTP 服务运行,客户端通过网络请求调用,适合远程部署。
在 LangChain 集成中,两种方式都支持。本文的示例先走 stdio,因为最简单;后面会给出远程 Mode 的思路。
3.3 MCP 与 Function Calling 的区别
很多读者会问:我直接用 Function Calling 不就行了吗?为什么要引入 MCP?
两者的定位并不冲突。Function Calling 是模型的一种输出能力,它解决的是“模型如何表达调用意图”的问题,但表达之后,谁来解析、谁来执行、执行结果如何传回,每个框架都不一样。MCP 解决的是“工具能力如何标准化暴露和发现”的问题。
换句话说,Function Calling 是模型侧的功能,而 MCP 是工具侧的协议。用 MCP Server 暴露工具之后,到了执行层仍然需要底层模型具备 Function Calling 能力,只是框架层面的适配被统一了。
3.4 MCP Server 中 Tool 的定义方式
使用 FastMCP 框架,定义一个工具非常简单:只需要在函数上加上装饰器,并写清楚 docstring 和类型注解。FastMCP 会根据函数的签名自动生成工具的描述信息,包括参数的 JSON Schema。
from fastmcp import FastMCP mcp = FastMCP("demo-server") @mcp.tool() def get_order_status(order_id: str) -> str: """查询订单状态 Args: order_id: 订单号 """ # 这里只是示例,实际项目中替换为真实业务逻辑 orders = { "1001": "已发货", "1002": "待支付", "1003": "已完成", } return orders.get(order_id, "订单不存在")工具注册好之后,MCP Server 会在握手阶段把所有工具列表返还给客户端,客户端再把这些工具交给模型供其决策。
4. 完整实战:手写 MCP Server 并接入 LangChain Agent
4.1 创建 MCP Server
我们写一个简单的“订单查询 + 天气查询”服务,虽然业务逻辑是模拟的,但完整覆盖了 MCP Server 的注册、启动、与客户端交互的关键步骤。
# 文件路径:mcp_server.py from fastmcp import FastMCP mcp = FastMCP("order-server") @mcp.tool() def get_order_info(order_id: str) -> str: """查询订单基本信息 Args: order_id: 订单号,例如 1001 """ orders = { "1001": "用户A 的订单,金额 199 元,状态:已发货", "1002": "用户B 的订单,金额 59 元,状态:待支付", "1003": "用户C 的订单,金额 899 元,状态:已完成", } return orders.get(order_id, "未查询到该订单") @mcp.tool() def get_weather(city: str) -> str: """查询指定城市的当前天气 Args: city: 城市名称,例如 北京、上海 """ weather_map = { "北京": "晴,25℃", "上海": "多云,28℃", "广州": "雷阵雨,30℃", } return weather_map.get(city, "暂未收录该城市的天气数据") if __name__ == "__main__": mcp.run(transport="stdio")按 Ctrl+C 结束服务即可。这个文件单独运行不会打印任何业务输出,因为 stdio 模式下它是在等待客户端的握手和数据请求。
4.2 验证 MCP Server 能正常工作
在写客户端之前,可以先用 mcp 官方提供的集成测试方式做一次快速验证。更常见的做法是直接通过 MCP Inspector 调试:
mcp dev mcp_server.pyMCP Inspector 会在浏览器里打开一个调试面板,你可以直接查看 Server 暴露了哪些工具,并手动输入参数进行调用测试。生产开发中这一步非常推荐,它能让你在接入 Agent 之前先确认工具本身没有逻辑错误。
4.3 在 LangChain 中加载 MCP 工具
接下来是整体链路中最关键的一步:使用 langchain-mcp-adapters 连接 MCP Server,并获取工具列表。
这里有两种常见用法:
- 使用
MultiServerMCPClient同时连接多个 MCP Server。 - 使用
load_mcp_tools针对单个 Server 做快速加载。
下面我们以 MultiServerMCPClient 为例,因为在一个真正的 Agent 项目中,通常会有多个 MCP Server 提供不同领域的能力。以 stdio 方式启动时,需要指定命令和参数:
# 文件路径:agent_client.py import asyncio from langchain_mcp_adapters.client import MultiServerMCPClient from langchain_openai import ChatOpenAI from langgraph.prebuilt import create_react_agent async def main(): # 1. 创建 MCP 客户端 async with MultiServerMCPClient( { "order": { "command": "python", "args": ["mcp_server.py"], "transport": "stdio", } } ) as client: # 2. 获取 MCP Server 暴露的工具 tools = client.get_tools() print("===== 可用工具 =====") for tool in tools: print(f"- {tool.name}: {tool.description[:50]}") print("====================\n") # 3. 创建模型实例 model = ChatOpenAI( model="deepseek-chat", api_key="your-api-key", base_url="https://api.deepseek.com", ) # 4. 创建 Agent agent = create_react_agent(model, tools) # 5. 测试对话 result = await agent.ainvoke( {"messages": [{"role": "user", "content": "订单1001现在是什么状态?"}]} ) print("===== 回复内容 =====") print(result["messages"][-1].content) if __name__ == "__main__": asyncio.run(main())4.4 运行与验证
先确保 mcp_server.py 和 agent_client.py 在同一个目录下,然后运行:
python agent_client.py如果一切正常,你会看到控制台先打印出从 MCP Server 拉取到的工具列表:
===== 可用工具 ===== - get_order_info: 查询订单基本信息 - get_weather: 查询指定城市的当前天气 ==================== ===== 回复内容 ===== 订单 1001 当前状态为:已发货。该订单属于用户A,金额为199元。到这里,一条完整的链路已经跑通了:
用户问题 → DeepSeek 模型 → 判断需要调用工具 → LangChain Agent → langchain-mcp-adapters → MCP Server → 返回结果 → 模型组织最终回答4.5 远程 MCP Server 怎么连
除了本地 stdio 方式,MCP Server 也可以作为一个 HTTP 服务启动,部署在远程服务器上。FastMCP 只需切换 transport 即可:
# 远程部署模式 if __name__ == "__main__": mcp.run(transport="http", host="0.0.0.0", port=8000)客户端连接方式需要调整:
async with MultiServerMCPClient( { "order": { "url": "http://localhost:8000/mcp", "transport": "streamable-http", } } ) as client: tools = client.get_tools()需要说明的是,不同版本的 langchain-mcp-adapters 对远程 HTTP 传输的参数写法可能有差异。如果你的版本不识别transport参数,请以对应版本文档为准。远程部署时还需要考虑鉴权、HTTPS、超时设置等问题,建议先在本地跑通再部署到线上。
5. 实战进阶:DeepSeek 接入与 Claude Code 配置
5.1 DeepSeek API 的基础调用方式
DeepSeek 的 API 采用了 OpenAI 兼容格式,这是它接入 LangChain 很方便的原因。
在 LangChain 中,不需要额外安装 deepseek 专用包,直接用 ChatOpenAI 并指定 base_url 即可。核心参数有三个:
- model:模型名称,常用的是 deepseek-chat 和 deepseek-reasoner。
- api_key:从 DeepSeek 开放平台申请。
- base_url:https://api.deepseek.com
一个最小调用示例如下:
from langchain_openai import ChatOpenAI model = ChatOpenAI( model="deepseek-chat", api_key="your-api-key", base_url="https://api.deepseek.com", ) resp = model.invoke("你好,请介绍一下你自己") print(resp.content)在本文的 Agent 示例中,DeepSeek 扮演的是“决策大脑”的角色:它负责理解用户意图、决定是否调用 MCP 工具、解析工具返回的结果。因为 Agent 循环需要多次调用模型,所以建议把超时时间和最大重试次数配置得合理一些。
5.2 使用 DeepSeek 的实用建议
在实际项目里,把 DeepSeek 接进 Agent 时建议考虑以下几点:
第一,system prompt 要写清楚工具使用的边界。比如“查询订单信息时,必须优先使用 get_order_info 工具”“如果工具返回‘未查询到’,不要编造订单状态”。在 Agent 应用中,模型幻觉的风险依然存在,尤其是工具返回空结果时,模型倾向于凭想象补全答案。
第二,deepseek-reasoner 适合需要深度推理的场景,但在 Agent 循环中它的响应时间更长、token 消耗也更大。如果只是做工具调用,deepseek-chat 往往性价比更高。
第三,为了节约 API 调用,可以在 prompt 里要求模型只在必要时调用工具。对于简单问候,不需要走工具链路。
5.3 Claude Code 如何配置 MCP
Claude Code 是 Anthropic 推出的命令行 AI 编程助手,它支持配置 MCP Server。安装好 Claude Code 之后,可以通过 MCP 相关命令来添加工具。常见的命令格式是:
claude mcp add demo-server -- python mcp_server.py执行后在 Claude Code 会话中,你就可以直接让 AI 使用这个 MCP Server 暴露的工具。
这里要提醒一点:Claude Code 的 CLI 版本更新非常频繁,MCP 命令的参数格式在过去几次版本中都有调整。如果你使用的版本提示命令不存在或参数不合法,优先运行 claude mcp --help 查看当前版本支持的命令。另外,生产项目中不要直接在全局配置里乱加 Server,建议每个 Server 使用独立的配置文件,并在项目级目录下管理。
5.4 关于“无法识别模型”类报错
在配置 Claude Code 接入第三方模型时,可能会遇到类似下面的报错:
"deepseek-v4-pro" is not a model this version of claude code recognizes这类问题的根本原因通常有两类:
一种是模型名称在当前 Claude Code 版本中还未被内置识别。Claude Code 会对模型白名单做校验,如果录入的模型名不在列表里,就会报错。针对这种情况,需要先确认 Model Name 是否填写正确,再确认 CLI 版本是否需要更新。
另一种是第三方模型的接入方式本身需要额外的兼容配置,而不只是改一个模型名。不同版本的 Claude Code 对第三方模型的环境变量要求不同,建议仔细阅读对应版本的官方配置说明,不要轻易相信社区的“更名大法”。
5.5 VSCode 中配置 Claude Code 的 MCP
很多开发者会把 Claude Code 集成到 VSCode 中使用。基本思路是在 VSCode 的终端里启动 Claude Code,然后在项目根目录维护 MCP 配置文件。当 MCP Server 发生变化时,需要在 Claude Code 中重新加载配置。
到这一步,你已经掌握了 Claude Code 使用 MCP 的最基本流程。更复杂的多 Server 管理、权限控制等能力,需要结合具体项目进一步探索。
6. 常见问题与排查思路
MCP + LangChain 的组合虽然简化了工具接入,但涉及 Agent、协议、模型三层依赖,踩坑点依然很多。下面整理几个高频问题。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| Agent 提示没有可用工具 | MCP Server 启动失败或握手超时 | 先单独运行 MCP Server,确认没有异常;再通过 MCP Inspector 验证工具列表 |
| 模型一直不调用工具 | 工具描述不清晰,或模型不支持 function calling | 优化工具 name 和 docstring,确认模型参数支持工具调用 |
| 工具执行成功但回答不准确 | 工具返回结果被模型忽略 | 检查工具返回格式,尽量返回结构化文本,并在 prompt 中强制要求结合工具结果回答 |
| the agent execution provider did not respond in time | Agent 执行环境超时,模型响应过慢 | 检查模型 API 延迟,适当调大超时时间,必要时更换更快的模型 |
| MCP Server 使用 stdio 时客户端卡住 | Server 进程输出非协议内容到 stdout | 确保 Server 中不要随意 print 内容,所有日志走 stderr |
| 远程 MCP 连接失败 | URL 不正确、端口未开放、鉴权缺失 | 用 curl 或浏览器访问 Server 端点验证联通性 |
| DeepSeek 报模型不存在 | 模型名拼写错误或账号无权限 | 从平台确认实际可用的模型名,不要使用推测的名称 |
下面针对几个重点问题展开说明。
6.1 模型不调用工具
这是 Agent 开发中最常见的问题。出现这个现象时,可以按顺序排查:
第一步,检查工具是否真的被加载到了 Agent 中。在代码里打印 tools 列表,确认 get_tools() 返回了预期的 Tool 对象。
第二步,检查模型是否支持 Function Calling。目前主流的 OpenAI 兼容模型基本都支持,但一些轻量模型表现不稳定。
第三步,检查工具的 description 是否足够具体。模型是通过描述来判断“什么时候用这个工具”的,如果你写的是“订单工具”,模型根本不知道里面到底是什么逻辑。更合理的写法是:“当用户询问订单状态、订单金额、发货进度时,使用该工具查询订单信息”。
6.2 Agent 循环超时
在 langgraph 的 create_react_agent 中,超时问题通常表现为整个节点长时间没有返回。可能是模型 API 响应慢,也可能是工具本身执行慢。
常用做法有:
- 给模型配置合理超时,比如 ChatOpenAI 中的 timeout 参数。
- 在工具函数内部实现自己的超时保护,避免外部服务无响应拖垮整个 Agent。
- 给 Agent 节点设置最大执行步数,防止模型陷入反复调用工具的循环。
6.3 MCP Client 获取不到工具
如果你确认 MCP Server 本身能跑通,但客户端始终拿不到工具,优先检查两件事:
一是 transport 类型是否匹配。Server 端用的 stdio,客户端却配置成 streamable-http,那一定连不上。
二是 Python 环境是否一致。使用 stdio 方式时,客户端会尝试用python命令启动 Server。如果客户端运行在虚拟环境 A,而命令python指向的是全局环境 B,且 B 中没有安装 fastmcp,Server 就会启动失败。
解决方案是在客户端配置中把 command 写清楚。例如在虚拟环境中可以使用/path/to/.venv/bin/python这样的绝对路径,而不是笼统的 python。
7. 最佳实践与工程建议
7.1 工具设计要“小而专”
一个工具只做一件事,工具名和描述要面向“模型能理解”来写。不要写一个“万能工具”,参数又长又复杂,模型很容易给错参数。比如把“查订单”和“退款”拆成两个工具,就比定义一个带 action 参数的 order_tool 更可靠。
在 MCP Server 中,docstring 的写法直接影响工具描述质量。建议采用以下模板:
@mcp.tool() def cancel_order(order_id: str, reason: str = "") -> str: """取消一个尚未发货的订单 Args: order_id: 需要取消的订单号 reason: 取消原因,可选 """ # 业务逻辑 ...7.2 日志与错误处理
MCP Server 在 stdio 模式下,所有标准输出都会被协议消息占用,所以日志只能输出到 stderr。你在 Server 内部使用 print 调试时可能不会立刻报错,但会导致客户端解析消息异常。建议统一使用 logging 模块输出到 stderr。
工具函数内部要做好异常捕获,把错误信息转换成模型能读懂的描述。不要直接把 Python traceback 返回给模型,模型会被大量报错信息干扰,正确的做法是返回一句话描述,比如“订单取消失败:订单状态不允许取消”。
7.3 环境变量与密钥管理
在 Agent 项目中,模型 API Key、MCP Server 的访问密钥都属于敏感信息,不要硬编码在代码里。可以使用 .env 文件配合 dotenv 管理,也可以通过 CI/CD 的密钥管理能力注入。
# .env DEEPSEEK_API_KEY=sk-xxxx MCP_SERVER_TOKEN=xxxx在实际项目中要遵循最小权限原则:Agent 能访问的工具范围必须控制,不是所有工具都应该暴露给所有用户。尤其是在涉及支付、退款、删除类操作时,必须在上层做鉴权和二次确认。
7.4 LangGraph 状态与可观测性
相比简单的 AgentExecutor,LangGraph 最大的优势是状态可观测。你可以给 Agent 加上节点级别的日志,打印每次模型输出和工具调用结果,这样定位“模型为什么答错”会容易很多。生产环境建议把工具调用的入参和出参记录到日志平台,方便后续分析和审计。
7.5 多 MCP Server 的隔离管理
当一个项目需要连接多个 MCP Server 时,不要把所有工具一股脑塞给 Agent。工具越多,模型选择错误工具的概率越大。建议按 Domain 拆分组,比如“订单域一组”“用户域一组”,然后根据具体任务决定加载哪些组。LangChain 的 MultiServerMCPClient 天然支持这种隔离方式,每个 Server 对应一个命名空间。
8. 总结与学习路线
写完这个 Demo,你已经完整走过了 MCP Server 开发、LangChain Tool 加载、Agent 编排、第三方模型接入、客户端配置这几条核心路径。回顾一下,本文的价值不在于那几十行代码,而在于把 MCP 放进了一个真实的 Agent 体系中,让你知道每一步发生在协议层的哪个位置。
如果你接下来要继续深入,建议按这个顺序学习:
第一,把 MCP Server 接入真实业务系统。替换订单查询里的模拟数据,接入数据库或后端 API,试试鉴权、超时、错误恢复这些真实场景。
第二,深入研究 LangGraph 的复杂编排。create_react_agent 只是起点,生产中会遇到多轮工具调用、条件分支、状态持久化等问题,这些都需要对 LangGraph 的图结构有更深入的理解。
第三,对比不同 Agent 框架的设计取舍。你可以在 LangChain、Claude Code、自研 Agent 框架中各自接入同一个 MCP Server,体会协议带来的“一次开发、多处复用”这一核心价值。
最后分享一个落地经验:在把 Agent 接入线上系统之前,一定要先为每个工具写好边界说明和失败预案。Agent 的能力上限由模型决定,但可靠性下限由工具设计和异常处理决定。把工具这层做扎实了,后续换模型、加能力都会轻松很多。如果这篇文章对你有帮助,欢迎收藏备用,也欢迎在评论区聊聊你在 MCP 接入过程中遇到的具体问题。