本章涵盖以下内容:
- 理解模型上下文协议(Model Context Protocol,MCP)的用途与底层架构
- 构建并对外暴露你自己的 MCP 服务器,并以一个实用的天气工具为例进行说明
- 在应用中测试和使用 MCP 服务器及其相关工具
- 将远程 MCP 工具与本地工具一起集成到智能体中
构建能够稳定访问并使用外部上下文的 AI 智能体,是应用开发者面临的核心挑战之一。直到最近,要从多个来源集成数据,通常都意味着要把每个来源各自封装成一个工具,而且往往还要使用不同的协议。这导致这项工作重复、耗时,并且会在不同团队之间被一遍又一遍地重复实现。
由 Anthropic 提出的模型上下文协议(MCP)解决了这个问题。它定义了一种统一方式,使各类服务可以通过 MCP 服务器暴露工具。智能体,也就是 MCP 主机(MCP host),通过 MCP 客户端连接到这些服务器,发现并调用远程工具,就像调用本地工具一样简单。这使集成工作被转移到它本该发生的地方——数据源头——从而让开发者能够把注意力放在构建强大的智能体上,而不是一遍遍重复实现相同的包装层。一旦连接完成,MCP 工具就可以无缝嵌入现有的智能体架构之中。
自 2024 年底发布以来,MCP 很快就成为事实标准。OpenAI 和 Google 等主要大语言模型(LLM)提供商已经在它们的 API 和 SDK 中采用了 MCP,同时,越来越多的公司和社区也开始将服务以 MCP 服务器的形式发布。如今,公开的 MCP 门户上已经有成千上万种工具可供使用,只需极少的工作,就能把它们集成进新的或已有的项目中。
在本章中,你将亲手实践 MCP。我们将讲解协议与架构的核心概念,了解不断扩展的生态系统,并带你认识官方与社区提供的 MCP 服务器。之后,你将构建自己的 MCP 服务器——从一个基于 AccuWeather 的天气数据示例开始——对它进行测试,并将其集成到一个智能体应用中。最后,我们还会看看如何把远程 MCP 工具与本地逻辑结合起来,如何配置客户端,以及如何遵循可靠集成的最佳实践。
随着 MCP 的采用范围不断扩大,构建和使用 MCP 服务器的能力,正逐渐成为开发者和服务提供方的关键技能。下面就让我们深入看看,MCP 是如何塑造下一代富上下文 AI 应用的。
13.1 MCP 服务器简介
现代 AI 智能体依赖外部上下文来源——数据库、API,以及各种专门化服务。传统上,这些来源都必须先被封装成智能体可调用的工具,通常还要通过 LLM 支持的临时性协议接入。这种做法虽然可行,但代价不小:每个团队最终都要自己编写和维护一套类似的封装层,导致重复劳动,并在整个生态中形成不一致的集成方式。
MCP 走的是另一条路。它不是让每个开发者都重复造轮子,而是允许上下文提供方自己通过统一协议,把数据和服务以工具的形式暴露出来。随后,智能体就可以直接消费这些工具,而无需额外编写定制的胶水代码。正如图 13.1 所示,这种统一方式消除了智能体开发中的大量摩擦,同时也促成了一个更丰富、更可复用的生态系统。
图 13.1 MCP 主机进程通过 MCP 客户端连接多个本地和远程 MCP 服务器,每个服务器都暴露由不同资源支撑的工具。这种架构使智能体能够通过标准化的工具接口,灵活访问本地文档和在线服务。
13.1.1 问题:大规模上下文集成
正如我们在前面几章中看到的,LLM 智能体通常通过注入到请求中的工具来消费外部数据。每增加一个新的上下文来源——无论是天气 API、文档数据库,还是搜索服务——都意味着要再创建一个包装工具,并将其接入智能体,而且通常还要遵循略有差异的约定和协议。这个过程不仅重复,而且相同的工作还会在无数团队中被不断复制。
你可以想象一下:每个智能体开发者都要花时间去包装同一批公开 API,或者每个组织都要为标准服务自己手工实现一遍集成。这种方式根本无法扩展,尤其是在可用工具和上下文来源的种类不断增长的情况下更是如此。
13.1.2 解决方案:模型上下文协议
MCP 通过提供一种标准化方式来应对这一挑战,使数据和服务提供方可以通过 MCP 服务器暴露工具。这样一来,就不再需要每个智能体都单独封装每一项服务;开发者只需“订阅” MCP 服务器,并使用它们暴露出来的工具即可,额外工作量极小。该协议定义了一种经典的客户端/服务器架构,正如你之前在图 13.1 中看到的那样。
在这个架构中,MCP 主机进程——也就是智能体或应用——通过 MCP 客户端连接到一个或多个 MCP 服务器。每个 MCP 服务器都可以暴露一组工具,而智能体只需要使用其中自己需要的那些。这种架构与 REST API 和 WebSocket 非常相似:它们同样通过客户端消费的方式暴露端点,因此也非常容易在需求变化时增添或替换新的上下文来源。
正如你在前面的图 13.1 中也看到的,MCP 服务器既可以是远程的(通常在生产环境中通过 Streamable HTTP 访问),也可以是本地的(通常通过标准输入/输出,即 STDIO 访问,常用于开发阶段,或者访问诸如本地文件之类的本地资源)。在大多数情况下,也包括本章中,我们都假设你使用的是远程 MCP 服务器(即便这些服务器其实运行在你自己的电脑上)。
一旦配置完成,来自 MCP 服务器的工具会以与本地工具完全相同的方式集成到你的智能体中,遵循的也是你在前几章中学到的同一套工具调用协议。如果你的智能体设计得模块化且架构清晰,那么通常甚至不需要修改任何业务逻辑,就可以支持远程 MCP 工具。
注意 MCP 的能力并不止于工具,它还可以标准化提示词、文件以及其他资源的共享方式。出于本章的目的,我们主要关注工具暴露,但如果你想深入了解,可以参阅 Anthropic 发布的原始文章 Introducing the Model Context Protocol。
13.1.3 MCP 生态系统
自 2024 年底推出以来,MCP 迅速获得了广泛关注。OpenAI 和 Google 等领先的 LLM 提供商都已采用 MCP,并将其支持集成进自己的 API 和智能体 SDK 中。在服务提供方这一侧,越来越多的公司开始使用 MCP 服务器来包装自己的服务和数据,使它们可以立刻成为适用于各类智能体和应用的“AI-ready”能力。
与此同时,也出现了多个公开的 MCP 服务器门户,使人们能够很容易地找到几乎适用于任何需求的工具。表 13.1 列出了一些最知名的目录。
表 13.1 MCP 服务器门户
| Portal | Description |
|---|---|
https://github.com/modelcontextprotocol/servers | Anthropic 官方 MCP 门户,列出官方和社区服务器 |
https://mcp.so/ | 一个由社区驱动的目录,收录了 16,000 多个服务器 |
https://smithery.ai/ | 一个拥有 5,000 多种工具的门户,其中大多数都兼容 MCP |
https://mcpservers.org/ | 一个约有 1,500 个服务器的集合 |
有了这样一个广泛且持续增长的生态系统,智能体越来越能够依赖一个共享的工具库——这些工具可能是企业内部构建的,也可能由大型公司提供,或者来自社区共享。
在了解了 MCP 服务器背后的动机、架构和生态系统之后,我们已经准备好进入下一步:看看如何在真实应用中构建、暴露和使用这些工具。在接下来的几节中,我们将进一步探讨如何创建你自己的 MCP 服务器,以及如何把 MCP 工具无缝集成到你的智能体中。
13.2 如何构建 MCP 服务器
既然我们已经清楚了解了 MCP 服务器是什么,以及它们在现代 AI 智能体生态中的角色,那么下一步就是学习如何真正构建、部署和集成 MCP 服务器。本节将带你了解关键资源、官方工具以及最佳实践,帮助你创建健壮的 MCP 服务器,并让智能体和应用可以使用它们。
13.2.1 MCP 服务器开发的关键资源
在进入代码之前,先熟悉一下 MCP 开发所依赖的基础文档和工具是非常重要的。该协议的官方中心站点是 https://modelcontextprotocol.io,其中提供了关于架构、设计原则、教程以及完整协议规范的大量信息。无论你是刚接触 MCP,还是想了解更高级的能力,这个网站都应当是你的起点。
提示 请特别关注 MCP 协议规范(https://modelcontextprotocol.io/specification)。这个规范会定期更新,并详细说明协议的方方面面——包括传输机制、安全考量以及最佳实践。规范本身与具体技术无关,因此无论你使用哪种语言或平台,它都具有参考价值。
虽然理解协议本身至关重要,但并不建议你在自己的应用中从零开始实现 MCP。那样做不仅意味着重复大量工作,而且你最终很可能会重新实现成熟 SDK 已经解决的问题。因此,你应该使用 MCP 官方提供的、针对具体语言的 SDK 之一。
13.2.2 官方语言专用 MCP SDK
MCP 的官方 GitHub 仓库汇集了多种资源,其中尤为重要的是针对多个主流编程语言提供的官方 SDK,包括 Python、JavaScript、Java、Kotlin 和 C#。本书主要聚焦 Python,但同样的总体原则也适用于其他受支持语言。
FASTMCP 1
最初的 Python SDK,通常被称为 FastMCP 1,可在 https://mng.bz/dWpX 获取。这个库为社区提供了第一个用于在 Python 中构建和使用 MCP 服务器的健壮框架。
FASTMCP 2
在 FastMCP 1 的基础上,MCP 社区发布了 FastMCP 2——这是一次重大升级,旨在解决原始实现中的一些局限,并更好地与最新协议规范保持一致。FastMCP 2 带来了显著改进:
更容易部署并组合多个服务器
增强的安全特性
更好的客户端连接能力以及高级功能,例如动态工具重写
内建测试工具以及与其他库集成的挂钩机制
FastMCP 2 目前在 https://github.com/jlowin/fastmcp 上持续维护,你也可以在 https://gofastmcp.com/getting-started/welcome 找到完整文档和教程。后续的动手示例我们将使用 FastMCP 2,所以请把这些资源准备好,便于边学边查。
13.2.3 在 LLM 应用与智能体中使用 MCP 服务器
前几节重点讨论了如何构建 MCP 服务器,而理解如何在你自己的 AI 应用中使用这些服务器同样重要。得益于广泛采用,如今集成 MCP 工具已经变得非常直接,尤其是在 OpenAI 这样的平台上更是如此。
OpenAI 的 API 通过 Responses API 原生支持由公开 MCP 服务器提供的工具。你不仅可以发现并引用这些工具,OpenAI 还会为你执行它们——这在许多场景下消除了手写客户端代码的需要。
提示 建议查阅 OpenAI 关于远程 MCP 工具集成的官方文档。整个过程并不复杂,但你仍然应当认真考虑授权策略——是自动批准这类调用,还是通过交互式方式批准。
在许多企业环境中,MCP 服务器可能只在组织内部网络中可用,因此你不能指望 OpenAI 的 Responses API 替你执行这些远程工具。对于这类场景,主要有两种做法:
使用 FastMCP 客户端。官方 FastMCP SDK 提供了客户端能力,可直接连接、认证并消费 MCP 服务器暴露的工具。
利用 LangChain / LangGraph 集成。如果你使用 LangGraph 或 LangChain 来开发智能体,可以使用 LangChain 的 MCP 客户端库——特别是 MultiServerMCPClient 类——通过一个简单的配置接口,轻松聚合并消费来自多个 MCP 服务器的工具。
在接下来的部分中,我们将实际演示这两种方式。你会学到如何构建、测试,并将一个实用的 MCP 服务器集成到智能体工作流中——无论你是直接使用 SDK,还是采用 LangChain 这类现代智能体框架。
13.3 构建一个天气 MCP 服务器
在了解了 MCP 服务器是什么、它们的用途,以及相关库与生态之后,现在是时候亲手构建一个了!在本节中,我们会把前几章中用于构建智能体的模拟天气工具,替换为一个基于 AccuWeather REST API 的真实世界天气 MCP 服务器。我们还会把这个服务器集成进前面构建过的某个智能体方案中。你将一步一步看到如何构建、测试,并将该 MCP 服务器连接到你的智能体。
13.3.1 实现 MCP 服务器
我们先从把之前的模拟天气工具替换为一个真正的 MCP 服务器开始,这个服务器将暴露来自 AccuWeather 的实时天气数据。在实现代码之前,请先访问 AccuWeather 开发者门户,并在 https://developer.accuweather.com/signup 免费注册,以获取一个 API key。
注册完成后,你会被重定向到订阅页面(https://developer.accuweather.com/subscriptions),在那里你会看到你的 Default App 以及与之关联的 API key。复制这个 key。接着,把它加入你项目的 .env 文件中,如下所示(请用你自己的真实 key 替换占位符):
ACCUWEATHER_API_KEY=<Your API key>
现在,你已经准备好实现一个真正暴露天气服务的 MCP 服务器了。在 ch11 文件夹下创建一个名为 mcp 的目录,然后在其中创建一个空的 Python 脚本 accuweather_mcp.py。下面的代码清单展示了 MCP 服务器的实现,它改编自这个 GitHub 仓库:https://github.com/adhikasp/mcp-weather。
代码清单 13.1 AccuWeather MCP 服务器
import os
import json
from typing import Dict
from fastmcp import FastMCP
from dotenv import load_dotenv
from aiohttp import ClientSession
load_dotenv() #1
mcp = FastMCP("mcp-accuweather") #2
@mcp.tool(description="""Get weather conditions
for a location.""") #3
async def get_weather_conditions(location: str) -> Dict:
"""Get weather conditions for a location."""
api_key = os.getenv("ACCUWEATHER_API_KEY") #4
base_url = "http://dataservice.accuweather.com"
async with ClientSession() as session:
location_search_url = f"{base_url}/locations/v1/cities/search"
params = { #5
"apikey": api_key,
"q": location,
}
async with session.get(location_search_url,
params=params) as response:
locations = await response.json() #6
if response.status != 200:
raise Exception(f"""Error fetching location
data: {response.status}, {locations}""")
if not locations or len(locations) == 0:
raise Exception("Location not found")
location_key = locations[0]["Key"] #7
current_conditions_url = \
f"{base_url}/currentconditions/v1/{location_key}"
params = { #8
"apikey": api_key,
"details": "true"
}
async with session.get(current_conditions_url,
params=params) as response:
current_conditions = \
await response.json() #9
if current_conditions and len(current_conditions) > 0:
current = current_conditions[0] #10
current_data = {
"temperature": {
"value": current["Temperature"]["Metric"]["Value"],
"unit": current["Temperature"]["Metric"]["Unit"]
},
"weather_text": current["WeatherText"],
"relative_humidity": current.get("RelativeHumidity"),
"precipitation": current.get("HasPrecipitation", False),
"observation_time": current["LocalObservationDateTime"]
}
else:
current_data = "No current conditions available"
return { #11
"location": locations[0]["LocalizedName"],
"location_key": location_key,
"country": locations[0]["Country"]["LocalizedName"],
"current_conditions": current_data,
}
if __name__ == "__main__":
mcp.run(transport="streamable-http", #12
host="127.0.0.1",
port=8020, path="/accu-mcp-server")
#1 加载环境变量
#2 初始化 FastMCP
#3 定义 MCP 工具
#4 获取 AccuWeather API key
#5 位置搜索参数
#6 获取地点列表
#7 获取 location key
#8 当前天气参数
#9 获取当前天气状况
#10 格式化当前天气结果
#11 返回结构化内容
#12 运行 MCP 服务器
这个实现依赖 fastmcp 包(也就是 FastMCP 2),它应当已经安装在你的虚拟环境中,因为它已列在 requirements.txt 文件里。实现的核心逻辑并不复杂:它先调用底层的 AccuWeather 地点搜索 REST 接口来解析用户输入,然后再通过 AccuWeather 的 API 查询当前位置的天气状况。
现在,请打开一个新终端(无论是在 VS Code 里,还是单独终端中),激活你的虚拟环境,进入 mcp 文件夹,并运行服务器:
C:\Github\building-llm-applications\ch11>env_ch11\Scripts\activate
(env_ch11) C:\Github\building-llm-applications\ch11>cd mcp
(env_ch11) C:\Github\building-llm-applications\ch11\mcp>python accuweather_mcp.py
你会看到 MCP 服务器开始启动,最终输出类似如下内容:
INFO: Started server process [20712]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8020 (Press CTRL+C to quit)
恭喜!你的第一个 MCP 服务器已经成功运行起来了。
13.3.2 使用 MCP Inspector 试用 MCP 服务器
想要以交互方式快速测试你刚刚创建的 MCP 服务器,最快的方法之一就是使用 MCP Inspector。这个工具提供了一个友好的界面,让你可以连接到任意 MCP 服务器、查看其暴露的工具,并执行实时查询,而无需编写任何客户端代码。整个过程非常直接,MCP Inspector 也是在把服务器集成进应用之前建立信心的绝佳方式。
安装 MCP Inspector
要开始使用,你需要先在电脑上安装 MCP Inspector。MCP Inspector 是一个 Node.js 应用,因此请先确保你已经安装了 Node.js。如果没有,可以从 https://nodejs.org 下载并安装。
Node.js 准备好之后,打开一个新的命令提示符或终端。为了保持工具组织清晰,建议你在项目目录下新建一个文件夹,比如在 ch11 下创建一个 mcp-inspector 文件夹。然后,运行以下命令,通过 npx 启动 MCP Inspector:
c:\Github\building-llm-applications\ch11\mcp-inspector>npx @modelcontextprotocol/inspector
在安装过程中,系统会提示你确认是否继续(你看到的版本号可能略有不同):
Need to install the following packages:
@modelcontextprotocol/inspector
Ok to proceed? (y) y
确认之后,安装会继续,随后 MCP Inspector 会自动在浏览器中启动:
Starting MCP inspector...
⚙️ Proxy server listening on localhost:6277
🔑 Session token: 8722a69c8ccf491da9862c288957a8cb4451b88d1f5e0539767d2b601d42c1e6
Use this token to authenticate requests or set DANGEROUSLY_OMIT_AUTH=true
to disable auth
🚀 MCP Inspector is up and running at:
http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=8722a69c8ccf491da9862c288957a8
cb4451b88d1f5e0539767d2b601d42c1e6
🌐 Opening browser...
连接到你的天气 MCP 服务器
当 MCP Inspector 运行起来之后,浏览器通常会自动打开一个新标签页,显示 MCP Inspector 界面,类似图 13.2 所示(等到本书出版时,界面可能会略有变化)。
图 13.2 安装完成后的 MCP Inspector
在界面左侧,你会看到多个选项(见图 13.3)。要连接到你的天气 MCP 服务器,请进行如下配置:
Transport Type:填写 Streamable HTTP。
URL:填写 http://127.0.0.1:8020/accu-mcp-server。
Connection Type:选择 Via Proxy。
Authentication:点击 Authentication 面板,并将认证开关切到左侧,以关闭认证。
图 13.3 连接到天气 MCP 服务器后的 MCP Inspector
配置完成后,点击 Connect 按钮。MCP Inspector 随即会尝试连接你的 MCP 服务器。连接成功后,你将在左侧面板看到绿色的 Connected 状态,就像图中显示的那样,这表示你的 MCP 服务器已经成功运行并且可以访问。
注意 根据你的系统环境,你可能需要使用 http://localhost:8020/accu-mcp-server,而不是 http://127.0.0.1:8020/accu-mcp-server。
浏览并测试天气工具
MCP Inspector 提供多个标签页,用于浏览 MCP 服务器可能暴露的不同能力:Tools、Prompts 和 Resources。对于你的天气 MCP 服务器而言,重点是 Tools 标签:
点击屏幕顶部的 Tools 标签。
点击 List Tools 按钮。
你应该会在列表中看到 get_weather_conditions 工具。如果点击它,右侧将出现一个专门对应此工具的面板,如图 13.4 所示。
图 13.4 MCP Inspector 显示 get_weather_conditions 工具以及右侧对应的面板
要试用这个新工具,请按以下步骤操作:
在 get_weather_conditions 面板中,在 Location 文本框里输入一个地点,例如 Penzance, UK。
点击 Run Tool。
工具会运行,并显示你所指定地点的当前天气状况,如图 13.5 所示。
图 13.5 MCP Inspector 输出显示 Penzance, UK 的当前天气状况,并确认该工具按预期正常工作
正如你所看到的,MCP Inspector 使你能够轻松验证 MCP 服务器及其暴露的工具是否工作正常,而且整个过程无需编写任何额外的客户端代码。
使用 MCP Inspector 之后的下一步
既然你已经通过 MCP Inspector 验证了自己的 MCP 服务器,并确认工具可以正常运行,那么接下来你就可以实现一个测试客户端应用。这会让你能够以编程方式连接到 MCP 服务器,并进一步将它集成到你的智能体方案中。
13.3.3 从测试 MCP 主机中消费 MCP 服务器
当你通过 MCP Inspector 验证了 MCP 服务器之后,下一步就是以编程方式与之交互。为此,你将实现一个简单的 MCP 主机——本质上是一个轻量级客户端——它会连接到你的天气 MCP 服务器并调用其功能。在 mcp 文件夹中创建一个新的 Python 脚本 test_accuweather_mcp.py,然后把代码清单 13.2 的代码复制进去。这种方式可以让你通过代码确认:你的 MCP 服务器可被发现,并且能够正确响应工具调用。
代码清单 13.2 为天气 MCP 服务器测试 MCP 主机
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport
import asyncio
transport = StreamableHttpTransport(
url="http://localhost:8020/accu-mcp-server") #1
client = Client(transport) #2
async def main():
async with client:
print(f"Client connected: {client.is_connected()}")
tools = await client.list_tools() #3
print(f"Available tools: {tools}")
if any(tool.name == "get_weather_conditions"
for tool in tools): #4
result = await client.call_tool(
"get_weather_conditions",
{"location": "Penzance, UK"}) #5
print(f"Call result: {result}") #6
print(f"Client connected: {client.is_connected()}")
if __name__ == "__main__":
asyncio.run(main()) #7
#1 将传输层设置为连接运行在 8020 端口上的 MCP 服务器的 Streamable HTTP
#2 创建 MCP 客户端
#3 列出 MCP 服务器暴露的工具
#4 检查该工具是否存在
#5 调用该工具
#6 打印调用结果
#7 运行 main 函数
如果你分析这段代码,会发现该脚本实例化了一个 MCP 客户端,并将其绑定到你前面实现的 MCP 服务器端点。它会先列出可用工具,然后执行一次对服务器暴露的天气工具的调用。
要运行这个测试主机,请再打开一个终端,激活你的虚拟环境,进入 mcp 文件夹,并运行脚本:
C:\Github\building-llm-applications\ch11>env_ch11\Scripts\activate
(env_ch11) C:\Github\building-llm-applications\ch11>cd mcp
(env_ch11) C:\Github\building-llm-applications\ch11\mcp>python test_accuweather_mcp.py
或者,你也可以直接以调试模式运行 test_accuweather_mcp.py。你会得到如下输出,其中展示了返回的天气数据(摄氏度):
Client connected: True
Available tools: [Tool(name='get_weather_conditions', title=None, description='Get weather conditions for a location.', inputSchema={'properties': {'location': {'title': 'Location', 'type': 'string'}}, 'required': ['location'], 'type': 'object'}, outputSchema={'additionalProperties': True, 'type': 'object'}, annotations=None, meta=None)]
Call result: CallToolResult(content=[TextContent(type='text', text='{"location":"Penzance","location_key":"322310","country":"United Kingdom","current_conditions":{"temperature":{"value":23.0,"unit":"C"},"weather_text":"Sunny","relative_humidity":71,"precipitation":false,"observation_time":"2025-07-13T10:56:00+01:00"}}', annotations=None, meta=None)], structured_content={'location': 'Penzance', 'location_key': '322310', 'country': 'United Kingdom', 'current_conditions': {'temperature': {'value': 23.0, 'unit': 'C'}, 'weather_text': 'Sunny', 'relative_humidity': 71, 'precipitation': False, 'observation_time': '2025-07-13T10:56:00+01:00'}}, data={'location': 'Penzance', 'location_key': '322310', 'country': 'United Kingdom', 'current_conditions': {'temperature': {'value': 23.0, 'unit': 'C'}, 'weather_text': 'Sunny', 'relative_humidity': 71, 'precipitation': False, 'observation_time': '2025-07-13T10:56:00+01:00'}}, is_error=False)
Client connected: False
请注意,这里不仅暴露出了可用工具,而且 get_weather_conditions 的调用结果还被封装在一个 CallToolResult 中,遵循了你在前面章节中见到过的标准协议。这说明,来自 MCP 服务器的结果是符合预期工具调用协议的。你也可以查看 MCP 服务器终端中的输出:
INFO: Uvicorn running on http://127.0.0.1:8020 (Press CTRL+C to quit)
INFO: 127.0.0.1:60339 - "POST /accu-mcp-server HTTP/1.1" 307 Temporary Redirect
INFO: 127.0.0.1:60339 - "POST /accu-mcp-server/ HTTP/1.1" 200 OK
INFO: 127.0.0.1:60342 - "POST /accu-mcp-server HTTP/1.1" 307 Temporary Redirect
13.4 将天气 MCP 工具集成到智能体中
既然你的天气 MCP 服务器已经运行起来,现在就该把它真正用到一个真实的智能体应用中了。在本节中,你将学习如何升级前几章中的旅行助手智能体:把它原先的模拟天气工具替换为一个完全可用、由 MCP 驱动的版本。我们将重点介绍如何把远程 MCP 服务器中的实时天气工具集成到旅行信息智能体中,让它能够在保留现有本地能力的同时,使用真实的 AccuWeather 数据,如图 13.6 所示。
图 13.6 旅行信息智能体通过远程 MCP 服务器中的真实天气工具得到增强,替换了原先本地的模拟天气工具
13.4.1 让旅行智能体使用实时天气数据
我们将修改旅行信息智能体,使其不再使用本地模拟天气服务,而是改为使用新的 AccuWeather MCP 服务器。为此,我们需要完成以下步骤:
移除 weather_forecast 工具以及 WeatherForecastService 类(以及其返回类型)的实现。
用一个客户端替代它们,该客户端连接到 AccuWeather MCP 服务器,并动态获取远程工具。
首先,把最初旅行智能体的代码从 main_03_01.py 复制一份,保存为 main_07_01.py,并以调试模式运行,以便回忆一下使用模拟工具时的预期行为。比如,当你询问 Penzance 的天气时:
UK Travel Assistant (type 'exit' to quit)
You: What's the weather in Penzance?
...
Assistant: [{'type': 'text', 'text': 'Currently, the weather in Penzance is windy with a temperature of 21°C. If you need a detailed forecast or more travel information, feel free to ask!', 'annotations': []}]
正如你看到的,这个模拟数据给出的回答是虚构的。现在,让我们把智能体连接到 MCP 服务器,以使用真实的实时天气信息。
13.4.2 集成 AccuWeather MCP 工具
首先,实现一个异步函数,用于实例化一个 AccuWeather MCP 服务器客户端。它会返回该服务器暴露的工具(在本例中只有一个):
async def get_accuweather_tools(): #1
mcp_client = MultiServerMCPClient({ #2
"accuweather": { #3
"url": "http://127.0.0.1:8020/accu-mcp-server",
"transport": "streamable_http"
}
})
return await mcp_client.get_tools() #4
#1 将获取 AccuWeather 工具定义为异步函数
#2 实例化 MultiServerMCPClient
#3 注册 AccuWeather MCP 服务器
#4 返回 MCP 服务器暴露的 AccuWeather 工具
13.4.3 更新智能体聊天循环
由于智能体现在会调用远程工具,因此你需要调整主聊天循环,以支持异步工具调用:
async def chat_loop(agent): #1
print("UK Travel Assistant (type 'exit' to quit)")
while True: #2
user_input = input("You: ").strip() #3
if user_input.lower() in {"exit", "quit"}: #4
break
state = {"messages": [HumanMessage(
content=user_input)]} #5
result = await agent.ainvoke(state) #6
response_msg = result["messages"][-1] #7
print(f"Assistant: {response_msg.content}\n") #8
#1 将 chat loop 定义为异步函数
#2 启动聊天循环
#3 获取用户输入
#4 检查用户输入是否为 "exit" 或 "quit",以退出循环
#5 创建初始 state,其中包含带有用户输入的 HumanMessage
#6 以异步方式调用智能体
#7 获取结果中的最后一条消息,也就是最终答案
#8 打印 assistant 的最终输出
13.4.4 组合本地工具与远程工具
在你的异步 main() 函数中,现在可以获取 AccuWeather 工具,并将其与本地的语义搜索工具组合起来,如下代码清单所示。
代码清单 13.3 组合本地工具与远程工具
class AgentState(TypedDict): #1
messages: Annotated[Sequence[BaseMessage], operator.add]
remaining_steps: RemainingSteps
async def main():
accuweather_tools = \
await get_accuweather_tools() #2
tools = [search_travel_info,
*accuweather_tools] #3
llm_model = ChatOpenAI(
model="gpt-5-mini",
use_responses_api=True) #4
travel_info_agent = create_react_agent( #5
model=llm_model,
tools=tools,
state_schema=AgentState,
name="travel_info_agent",
prompt="""You are a helpful assistant that can
search travel information and get the weather forecast.
Only use the tools to find the information you need
(including town names).""",
)
await chat_loop(travel_info_agent) #6
if __name__ == "__main__":
asyncio.run(main()) #7
#1 定义 AgentState 类
#2 获取 AccuWeather MCP 服务器工具
#3 将本地的 search_travel_info 工具与 AccuWeather MCP 服务器工具合并
#4 实例化 LLM 模型
#5 创建 travel_info_agent
#6 启动聊天循环
#7 以异步方式运行 main 函数
注意 现在,main 函数和 chat_loop 都变成了异步形式,这使你的智能体能够以极小的额外成本同时使用本地工具和 MCP 工具。
13.4.5 测试与验证
代码更新完成之后,你就可以以调试模式运行 main_07_01.py。在实例化语言模型(llm_model)的那一行设置一个断点,并检查 tools 列表。你应该会在输出中同时看到本地工具和远程工具:
[StructuredTool(name='search_travel_info', description='Search travel information about destinations in England.', args_schema=<class 'langchain_core.utils.pydantic.search_travel_info'>, func=<function search_travel_info at 0x000002141393D620>),
Tool(name='get_weather_conditions', title=None, description='Get weather conditions for a location.', inputSchema={'properties': {'location': {'title': 'Location', 'type': 'string'}}, 'required': ['location'], 'type': 'object'}, outputSchema={'additionalProperties': True, 'type': 'object'}, annotations=None, meta=None)]
继续运行代码,并尝试向助手询问 Penzance 的天气(当屏幕提示 You: 之后):
UK Travel Assistant (type 'exit' to quit)
You: What's the weather in Penzance?
Assistant: [{'type': 'text', 'text': 'The current weather in Penzance is light rain with a temperature of 17°C. The humidity is quite high at 94%.', 'annotations': []}]
正如你所看到的,AccuWeather MCP 服务器现在已经能够被智能体调用。你也可以查看 MCP 服务器所在终端中的输出,或者检查 LangSmith trace,以确认这个工具确实按预期被调用了。
13.4.6 用智能体处理复杂查询
最后,尝试一些更高级的、需要结合旅行信息与实时天气数据的推理型查询。请记得根据当前季节调整问题内容。例如:
You: Suggest two beach Cornwall towns with nice weather
Assistant: [{'type': 'text', 'text': 'Two beach towns in Cornwall are Newquay and St Ives. However, currently, Newquay is experiencing light rain with a temperature of 17°C, and St Ives has hazy sunshine with a temperature of 6°C. If you prefer nicer weather, St Ives would be the better choice at the moment.', 'annotations': []}]
你还可以继续尝试类似这样的查询:
You: Suggest two beach Cornwall towns with nice weather; keep trying until you find two with nice weather
通过集成由 MCP 服务器暴露的天气工具,你已经让你的智能体具备了提供真正实时、可操作信息的能力。这不仅展示了 MCP 的力量,也展示了如何把外部工具与本地智能体技能结合起来,构建更丰富、更有用的应用。
总结
-
模型上下文协议(MCP)提供了标准化的工具接口,使智能体无需编写定制集成代码,就能发现并消费这些工具。服务器通过统一的 JavaScript Object Notation–Remote Procedure Call(JSON-RPC)协议暴露能力,例如数据库访问、文件操作或 API 调用。
-
这些服务器往往由服务供应商自己提供。在构建自定义集成之前,应先检查你的目标服务是否已经有官方 MCP 服务器,例如 GitHub、Slack、Google Drive。
-
MCP 采用客户端/服务器架构。智能体(主机)向 MCP 服务器发送 JSON-RPC 2.0 请求,服务器执行实际操作,并以标准化格式返回结果。
-
单个智能体可以同时连接多个 MCP 服务器,把它们当作一个统一的工具生态来使用。智能体会发现每个服务器暴露的工具,并决定调用哪一个。
-
FastMCP 2(Python)和 TypeScript MCP SDK 都可用于构建 MCP 服务器。它们支持基于装饰器的工具定义、根据类型提示自动生成 schema,以及内建错误处理。
-
MCP Inspector 提供了一个 Web UI,使你可以在接入生产环境之前,以交互方式测试 MCP 服务器。这有助于在把服务器集成进智能体工作流之前先完成验证。
-
OpenAI 的 ChatGPT 和 API 原生支持 MCP 工具调用。智能体可以使用标准函数调用方式调用由 MCP 暴露的工具,而无需额外的桥接层。这消除了 LLM 提供商与 MCP 服务器之间的定制桥接代码。只要将智能体配置为连接 MCP 服务器,LLM 就会自动发现并调用可用工具。
-
LangChain 和 LangGraph 会自动将 MCP 工具转换为框架原生工具。智能体可以无缝混用 MCP 提供的工具与框架自身的工具。
-
例如,一个智能体可以先调用本地计算函数,再调用由 MCP 提供的数据库查询,然后再调用本地格式化函数——整个工作流中无需区分工具来源。
-
随着 MCP 逐渐成为向 AI 智能体暴露服务的标准,其采用率正在持续增长。Anthropic、GitHub、Slack 等主要平台都在为自己的 API 发布 MCP 服务器。
-
学习 MCP 架构与服务器开发,可以让你构建出能够连接这一不断扩展生态的智能体,而不必为每项服务都重新发明一遍集成模式。
-
MCP 服务器以独立进程形式运行,通过标准输入/输出(STDIO)或 HTTP 通信。对于本地开发,STDIO 更简单;对于跨网络的生产环境,则应使用带有适当认证机制的 HTTP。