DLAI-Anthropic-MCP-笔记-全-
DLAI Anthropic MCP 笔记(全)
001:1.课程介绍


欢迎来到《使用Anthropic构建具有丰富上下文的AI应用》课程。
本课程是与Anthropic合作开发的。在本课程中,你将学习MCP的核心概念以及如何在你的AI应用中实现它。
模型上下文协议,即MCP,是一个开放协议。它标准化了你的语言模型应用如何以工具和数据资源的形式获取上下文。
它基于客户端-服务器架构。该协议定义了运行在你自身应用内部的MCP客户端,与对外暴露工具、数据资源和提示模板的MCP服务器之间如何进行通信。
自Anthropic于2024年11月发布MCP以来,MCP生态系统一直在飞速发展。
我很高兴地宣布,本课程的讲师是Eddie Schic。他是Anthropic的技术教育主管。谢谢Andrew。我很兴奋能与你一起教授这门课程。
MCP起源于一个内部项目。当时我们认识到一个机会,可以扩展Claude Desktop的能力,使其能够与本地文件系统和其他外部系统交互。
我们发现我们开发的协议在许多有类似需求的AI应用中都非常有用。
为了让更多开发者能够使用它,我们发布了规范,并将其开发开放给开源社区。
MCP生态系统包含了越来越多由开源社区以及Anthropic的MCP团队开发的MCP服务器。
MCP是模型无关的,并且设计得易于接入多个应用。
假设你正在构建一个研究助手智能体,并且希望这个智能体能与你的GitHub仓库交互,从你的Google Drive文档中读取笔记,或许还能在你的本地系统中创建一些内容。与其编写自定义工具,你可以将你的智能体连接到GitHub、Google Drive和文件系统服务器。这些服务器将提供工具或API调用定义,并处理工具的执行。
我将带你深入了解MCP协议的细节。我们首先将深入探讨MCP客户端-服务器架构的细节。然后,你将着手开发一个聊天机器人应用,使其兼容MCP。你将构建并测试一个MCP服务器,并将你的聊天机器人连接到它。你的MCP服务器将为你的聊天机器人提供工具、提示模板和资源。你还将把你的聊天机器人连接到其他受信任的第三方服务器,以扩展其能力。之后,你将复用你的MCP服务器。

并将其连接到其他MCP应用,例如Claude Desktop。

最后,你将学习如何远程部署你的MCP服务器。
我要感谢来自DeepLearning.AI的Harra Salami,他对本课程做出了贡献。
MCP是一项非常重要的技术。它让AI应用开发者能够更容易地将他们的系统连接到许多工具和数据资源。


对于构建工具和提供数据的团队来说,这项技术也让他们更容易地使自己构建的内容被众多开发者使用。


因此,这是一项值得学习的技术。下一个视频将讲解为什么以前将AI应用连接到资源如此困难,以及MCP如何解决这个问题。请前往下一个视频了解更多内容。
002:为什么需要MCP? 🤔
在本节课中,我们将学习模型上下文协议(Model Context Protocol, MCP)如何解决AI开发中的碎片化问题,并标准化AI应用与外部数据源之间的连接方式。
概述

模型的能力取决于提供给它的上下文。即使拥有最前沿的智能模型,如果它无法连接外部世界并获取必要的数据和上下文,其效用也会大打折扣。MCP作为一个开源协议,旨在标准化大型语言模型与工具及数据源的连接和工作方式。
核心目标:标准化而非重复发明
上一节我们介绍了MCP的基本概念,本节中我们来看看其核心目标。MCP的理念不是重新发明诸如工具使用之类的方法,而是标准化AI应用与数据源的连接方式。这类似于我们使用REST等协议来标准化Web应用与后端及其他系统的通信方式。
核心思想:MCP ≈ 标准化AI应用与外部系统的交互协议
MCP的价值:一次构建,处处使用
在当今世界,众多不同的模型需要与众多不同的数据源甚至彼此通信。MCP确保我们使用同一种“语言”,避免了为不同模型或数据源反复构建相同集成的困境。
以下是MCP带来的主要优势:
- 标准化交互:统一AI应用与外部系统的交互方式。
- 提高效率:构建一次集成,即可在多个地方使用。
- 借鉴成熟经验:MCP借鉴了其他旨在实现类似目标的协议思想,例如微软在2016年推出的语言服务器协议(LSP),它标准化了集成开发环境与语言特定工具的交互。





实践演示:MCP的强大能力



理论阐述之后,让我们通过一个快速演示来直观感受MCP的能力。演示中,仅用少量代码,我们就能为AI应用引入上下文。
在演示中,左侧使用Claude桌面应用,通过自然语言询问从GitHub仓库检索问题。右侧可以看到GitHub仓库。通过连接到一个提供GitHub数据的MCP服务器,以及另一个为项目管理工具Asana提供服务的MCP服务器,我们能够从GitHub读取数据,并请求在Asana中对特定问题进行分类和分配工单。
演示界面中有人参与循环,以验证要执行的操作。通过极少的代码,我们就能轻松地与外部数据源通信。
演示核心:自然语言指令 -> MCP服务器(GitHub/Asana)-> 执行操作并返回结果



MCP的架构优势:关注点分离

如前所述,没有MCP也能实现这些功能。但在构建这些集成时,问题随之而来:工具存储在哪里?自定义提示词存储在哪里?数据访问层和身份验证逻辑又存储在哪里?
我们发现许多不同的团队在重复造轮子,许多不同的AI应用以不同的方式与相似的数据源通信。MCP不仅是模型无关的,而且完全开源。这些工具和数据连接性由开源社区提供,或者您可以自己构建。
MCP以非常清晰的方式分离了关注点。我们构建或使用MCP兼容的应用程序,并根据需要连接到许多不同的服务器以获取特定类型的数据访问。


我们可以拥有用于数据存储的服务器、用于客户关系管理工具(如HubSpot或Salesforce)的服务器,甚至用于版本控制等功能的服务器。目标是通过自然语言与这些数据存储对话,而无需自己编写所有逻辑。
MCP服务器的美妙之处在于它可以在许多不同的应用程序中重用。我们可以使用参考服务器,甚至可以内部构建并在多个应用程序之间共享。
MCP的受益者
MCP为不同的受众带来了诸多好处:

- 对于应用开发者:只需极少工作即可连接到MCP服务器。
- 对于API开发者:构建一次MCP服务器,即可在任何地方被采用。
- 对于AI应用用户:MCP背后的理念可以被抽象化,您只需提供一个MCP服务器的URL,就能将所需的数据访问引入您的应用。
- 对于企业和大型组织:可以考虑关注点分离和构建独立集成的好处,以便不同团队使用。
生态发展与常见问题
MCP生态系统正在快速发展。我们不仅看到大公司的开发,也看到前沿初创公司的参与,开源社区和私有领域都在构建许多不同的服务器。支持MCP的SDK(软件开发工具包)用多种语言编写,由开源社区和许多不同的公司及AI开发者共同推动。我们看到了跨Web应用、桌面应用甚至智能体产品的MCP兼容应用。
在结束之前,让我们回答几个关于MCP的常见问题。

谁编写这些MCP服务器(从GitHub到Asana到Google Drive)?
任何人都可以。您作为开发者可以自己构建,也可以直接使用社区采纳的服务器。在接下来的课程中,我们将看到如何构建MCP服务器,并会亲自构建几个。
MCP服务器类似于API吗?
可以这样理解。您可以将MCP服务器视为API之上的网关或包装器。如果您不想直接调用API,可以使用自然语言,让MCP服务器为您处理。MCP服务器支持工具使用,但这只是其功能的一部分。服务器为您提供可用的函数和模式。正如我们将在下一课中看到的,MCP提供的功能远不止于此。
总结

本节课中,我们一起学习了为什么需要模型上下文协议(MCP)。我们看到了一个非常精彩的演示,展示了它如何以极少量工作实现强大功能。在下一课中,我们将开始探索MCP的内部工作原理,介绍主机、客户端和服务器的概念,并讨论协议中的一些底层原语,如资源、工具和提示。
003:MCP架构详解 🏗️
在本节课中,我们将深入探讨模型上下文协议(MCP)的架构。我们将了解MCP如何通过客户端-服务器模型工作,以及它提供的核心功能,如工具、资源和提示模板。通过理解这些基础概念,你将能够更好地利用MCP来构建连接外部数据源的强大AI应用。
架构概述
上一节我们介绍了MCP在连接AI应用与外部数据源方面的实用性。本节中,我们来看看支撑这一切的底层架构。
与许多其他协议类似,MCP遵循客户端-服务器架构。其中,MCP客户端与MCP服务器维持着一对一的连接。两者之间通过MCP自身定义的消息进行通信。
这些客户端存在于一个宿主环境中。宿主可以是Claude Desktop、Claude.ai等应用程序。宿主的职责是存储和维护所有客户端及其与MCP服务器的连接。
让我们更深入地看看这个结构:
- 宿主:希望访问数据的LLM应用。
- 服务器:通过MCP协议暴露特定能力的轻量级程序。
很快,我们将开始构建自己的服务器、客户端以及包含多个客户端的宿主。虽然相关代码会涉及一些底层细节,但本节的目标是理解架构。这样,当你使用Claude Desktop、Cursor或Windsurf等工具时,就能明白其背后的工作原理。
核心原语
在讨论客户端和服务器的具体职责前,我们先来了解协议的一些原语或基本组成部分。
工具 🛠️
如果你熟悉工具调用,那么MCP中的工具看起来会非常相似。工具是可由客户端调用的函数。
这些工具允许执行检索、搜索、发送消息和更新数据库记录等操作。工具通常用于那些可能需要POST请求或某种修改操作的数据。


以下是使用Python MCP SDK声明一个工具的示例代码:
@mcp.tool()
def list_tables() -> list[str]:
"""返回数据库中的所有表名。"""
# ... 执行逻辑 ...
return table_names
资源 📄
资源与GET请求更为相似。资源是服务器暴露的只读数据或上下文。
你的应用可以选择是否使用这些资源,但不一定非要将其纳入上下文。资源的例子包括数据库记录、API响应、文件、PDF等。
以下是声明一个资源的示例:
@mcp.resource(uri="demo://documents")
def get_documents() -> str:
"""返回文档列表。"""
return "文档1内容\n文档2内容"
对于动态信息,可以使用模板化资源:
@mcp.resource(uri="demo://documents/{doc_id}")
def get_document(doc_id: str) -> str:
"""根据ID返回特定文档。"""
return f"这是文档 {doc_id} 的内容。"
提示模板 💬

我们要探讨的第三个原语是提示模板。提示模板旨在完成一个非常合理的任务:减轻用户的提示工程负担。
你可能会有一个MCP服务器,其职责是查询Google Drive中的内容并进行总结等。但用户本身需要编写提示,才能以最高效的方式完成所有这些任务。提示模板是存在于服务器上的预定义模板,客户端可以访问它们,并在用户选择时提供给用户。这样就不强制用户编写整个提示并摸索提示工程的最佳实践了。
在接下来的课程中,我们将看到如何在服务器和客户端上构建工具、资源和提示模板。客户端的职责是发现资源和工具,而服务器的职责是将这些信息暴露给客户端。



实战演示
现在我们对这些原语有了基本了解,让我们通过一个宿主(Claude Desktop)来实际看看它们是如何运作的。我将连接到一个为SQLite暴露工具、资源和提示的MCP服务器。
连接后,我可以开始用自然语言与我的数据对话。例如,询问:“我有哪些表?每个表里有多少条记录?”
这时,Claude会连接到外部世界,并使用来自SQLite服务器的一个名为list_tables的工具。在请求中,我们看到没有发送动态数据。执行后,我们可以看到结果:有30个产品、30个用户和0个订单。


我们还可以进行更有趣的操作,例如利用工具生成可视化图表。输入:“基于产品表中的数据生成一个有趣的可视化图表。”
即使有拼写错误,系统也能查询到所需信息:找到表,运行必要的查询并获取数据。分析工具会分析数据并告诉我们许多商品的价格分布,以及存在少数高价商品。通过MCP,我们可以立即构建出非常有趣的应用程序。

接下来,让我们探索其他原语。在SQLite示例中,有一个“MCP演示提示”。这是一个从服务器发送过来的提示模板,用户只需传入一些动态数据即可。
例如,使用提示“用一些关于行星的数据来填充数据库”。当将其添加到提示中时,我们会立即看到一个由服务器提供的文本文件生成。这不是用户必须编写的内容,用户只需选择动态数据,然后运行该特定提示即可。这样,用户就可以使用经过充分测试和评估的提示,而无需自己动手。
在演示中,我们还看到了一个“数据洞察”或“业务洞察备忘录”,它会随着我们不断添加新数据而更新。这就是一个资源的例子。资源是动态的,可以随着应用程序中数据的变化而更新。我们不需要使用工具来获取这些信息,服务器会直接发送数据给客户端,由应用程序决定是否使用这些数据。

在这个小例子中,我们看到了宿主(Claude Desktop)、来自SQLite MCP服务器的各种工具、提示以及资源,它们共同允许我们执行非常强大的操作。

通信与传输
既然我们已经了解了如何使用MCP服务器的工具,接下来让我们谈谈如何实际创建它们。
MCP为多种语言提供了用于构建服务器和客户端的软件开发工具包。在本课程中,你将使用Python MCP SDK,它可以非常方便地声明工具、资源和提示。


现在我们对一些原语有了概念,让我们简要谈谈客户端和服务器之间的通信。
当客户端打开与服务器的连接时,会有一个初始化过程:发送请求、接收响应并发送通知以确认初始化。一旦初始化完成,就会发生消息交换。了解这些步骤很重要,因为在代码中你实际上会看到像initialize这样的方法。
客户端可以向服务器发送请求,服务器也可以向客户端发送请求,通知也可以双向发送。我们稍后会讨论其他一些协议,服务器可以通过这些协议向客户端采样或请求信息,通知也可以双向发送。最后,在通信结束时,连接会终止。
在进一步讨论连接和消息传递方式时,理解模型上下文协议的另一个部分很重要,那就是传输的概念。
传输处理客户端和服务器之间消息传递的机制。根据应用程序的运行方式,你将选择其中一种不同的传输方式,如果需要,也可以创建自己的传输方式。
- 对于本地运行的服务器,我们将使用标准输入/输出。
- 对于远程部署的服务器,我们可以在HTTP(及服务器发送事件)之间选择。在本课程后期部署远程服务器时,我们将使用可流式HTTP传输。
在录制本课程时,可流式HTTP尚未在所有SDK中得到支持。因此,在我们的示例中,我们将使用带有服务器发送事件的HTTP,以便让你了解其中的区别。
使用带有服务器发送事件的HTTP时,你需要打开一个有状态的连接,该连接保持开放以进行来回通信。对于某些类型的应用程序和部署,或者无状态部署,这并不适用。因此,在协议的新版本中,可流式HTTP传输允许有状态连接和无状态连接。
谈到我们的第一个传输方式——标准I/O,其过程涉及客户端将服务器作为子进程启动,服务器通过标准输入和标准输出与客户端一起进行读写操作。所有这些都将对我们抽象化,但重要的是要理解,在使用标准I/O时,这通常是在本地运行服务器时的做法。
当我们讨论远程传输时,你会看到基于协议的不同版本有不同的传输方式。远程服务器的原始传输涉及使用HTTP和服务器发送事件来建立有状态连接。在有状态连接中,客户端和服务器相互通信,请求之间没有关闭连接,数据可以在不同请求之间共享和记忆。
随着引入服务器发送事件的能力,服务器也能够将事件和消息发送回客户端。虽然这适用于多种应用程序,但许多应用程序在部署时既不是有状态的,也不需要是有状态的。事实上,有时为了扩展应用程序,使用短暂或无状态的服务器更高效,其中每个连接和每个请求都是不同的且不被记忆。
为了同时支持有状态和无状态连接,协议更新版本包含了一个新的传输方式,称为可流式HTTP。它允许将有状态连接的HTTP与服务器发送事件结合使用,或者将无状态连接的标准HTTP单独使用。
展望未来,可流式传输将是推荐和使用的方式,以便你可以支持无状态连接以及有状态连接。其工作方式是通过向某个端点(例如 /mcp)发送HTTP GET和POST请求来初始化请求,服务器返回响应。如果我们想选择加入或升级到服务器发送事件,我们可以继续发出可选的GET请求并来回发送通知。否则,我们发出POST请求并获取响应。
总结
本节课中,我们一起深入探讨了MCP背后的架构,从客户端、服务器和宿主,到演示如何使用一些最流行的原语,如工具、提示和资源。你还了解了一些用于发送数据的不同传输和机制。

现在,是时候让你亲自动手编写一些代码了。在下一课中,我们将了解一些我们将要使用的功能和工具,然后开始叠加MCP逻辑来构建我们自己的服务器,并最终构建客户端和宿主。
004:构建聊天机器人示例 🤖

在本节课中,我们将学习如何构建一个具备工具调用功能的聊天机器人。我们将使用Anthropic的Claude模型,并结合arXiv API来搜索和获取学术论文信息。

准备工作 🛠️
在开始使用MCP之前,确保你对大型语言模型的工具调用和提示工程有良好的基础。
现在,让我们开始编码。我们将从一个简单的聊天机器人应用开始,它利用arXiv来搜索论文并查找信息。如果你已经熟悉这部分内容,可以跳过本课,直接进入构建你的第一个MCP服务器。
导入必要的库
首先,我们需要导入一些必要的库。
import arxiv
import json
import os
from typing import List, Dict, Any
import anthropic
以下是每个库的作用:
arxiv:用于搜索arXiv上的学术论文。json:用于数据格式化。os:用于处理环境变量。typing:用于代码类型提示。anthropic:用于调用Anthropic的Claude模型。
定义常量与第一个工具函数
我将首先定义一个名为PAPER_DIRECTORY的常量,它是一个字符串“papers”。这将用于将信息保存到文件系统。

PAPER_DIRECTORY = "papers"
接下来,我们引入第一个函数。让我们来看看这个函数。
这个函数名为search_papers。它接受一个主题和一个结果数量参数(默认为5)。它的功能是在arXiv上搜索论文。如果你不熟悉,arXiv是一个开源存储库,包含从数学到科学等许多不同领域的已发表论文。
def search_papers(topic: str, num_results: int = 5) -> List[str]:
client = arxiv.Client()
search = arxiv.Search(
query=topic,
max_results=num_results,
sort_by=arxiv.SortCriterion.SubmittedDate
)
results = list(client.results(search))
if not os.path.exists(PAPER_DIRECTORY):
os.makedirs(PAPER_DIRECTORY)
papers_info = []
for paper in results:
paper_info = {
"id": paper.entry_id,
"title": paper.title,
"summary": paper.summary,
"pdf_url": paper.pdf_url,
"published": paper.published.isoformat()
}
papers_info.append(paper_info)
file_path = os.path.join(PAPER_DIRECTORY, "papers_info.json")
with open(file_path, 'w') as f:
json.dump(papers_info, f, indent=2)
return [paper['id'] for paper in papers_info]
我们将初始化客户端,然后开始搜索相关文章。获取搜索结果后,我们将创建一个目录(如果已存在则跳过),并将这些信息保存到一个名为papers_info.json的文件中。我们将处理每一篇论文并创建一个字典,然后将其写入文件。完成后,这个函数将返回一些论文ID。
让我们搜索一些关于“computers”的论文。
paper_ids = search_papers("computers")
print(paper_ids)
我们可以看到,信息被保存到了本地文件,并且我们获得了一系列ID,可以用来获取更多相关信息。
定义第二个工具函数
现在,我们引入第二个函数来利用这些论文ID。
我们将定义另一个名为extract_info的函数。它将接收一个论文ID,在我们的papers_info.json文件中查找,并返回关于该论文的信息。如果找不到,则返回一个字符串提示。
def extract_info(paper_id: str) -> str:
file_path = os.path.join(PAPER_DIRECTORY, "papers_info.json")
try:
with open(file_path, 'r') as f:
papers_info = json.load(f)
except FileNotFoundError:
return "No saved information for papers."
for paper in papers_info:
if paper['id'] == paper_id:
return json.dumps(paper, indent=2)
return f"No saved information for paper ID: {paper_id}"
我将获取第一个ID,然后运行这个函数,展示它的输出。
if paper_ids:
first_id = paper_ids[0]
paper_details = extract_info(first_id)
print(paper_details)
我们可以看到,返回的数据不仅包括论文标题,还有PDF的URL以及该论文的摘要。
我们将从这两个函数开始。然后,我们将开始将这些函数作为工具引入大型语言模型。
为模型定义工具列表
我们将能够为Anthropic的Claude模型传入一些工具。然后,我们将构建一个小型聊天机器人,它接收这些工具,并知道何时调用它们以及如何为这些函数返回数据。
让我们定义我们的工具列表。如果你熟悉工具调用,这应该不陌生。你创建的每个工具都需要有一个名称、描述以及它需要遵循的某种模式。
tools = [
{
"name": "search_papers",
"description": "Search for academic papers on arXiv based on a topic.",
"input_schema": {
"type": "object",
"properties": {
"topic": {"type": "string", "description": "The research topic to search for."},
"num_results": {"type": "integer", "description": "Number of results to return.", "default": 5}
},
"required": ["topic"]
}
},
{
"name": "extract_info",
"description": "Get detailed information and summary for a specific paper using its arXiv ID.",
"input_schema": {
"type": "object",
"properties": {
"paper_id": {"type": "string", "description": "The arXiv ID of the paper (e.g., '2401.12345v1')."}
},
"required": ["paper_id"]
}
}
]
记住,模型本身不会调用这些函数。我们实际上需要编写代码来调用这些函数并将数据传回模型。但这些工具的目的是允许模型扩展其功能。这样,模型就不会回答“我不知道”或产生幻觉,而是能返回我们想要的答案。
编写模型交互与工具执行逻辑
现在,让我们开始编写与大型语言模型交互和执行这些工具的一些逻辑。
我们引入一个工具映射。这里有一个函数,它将根据我们拥有的工具名称映射来调用底层的函数。
def execute_tool(tool_name: str, arguments: Dict[str, Any]) -> Any:
tool_map = {
"search_papers": search_papers,
"extract_info": extract_info
}
if tool_name in tool_map:
return tool_map[tool_name](**arguments)
else:
return f"Error: Tool {tool_name} not found."
你可以看到,我们为每个工具名称准备了一个字典,它们指向我们下面定义的函数。然后,这个方便的辅助函数会去调用特定的函数,并以各种传入的数据类型将结果返回给我们。
构建聊天机器人主循环
现在,让我们开始构建我们的聊天机器人。首先,引入环境变量中的API密钥,然后创建Anthropic客户端的一个实例。我们需要这个实例来调用我们的模型并获取返回的数据。
# 假设你的API密钥已设置在环境变量 ANTHROPIC_API_KEY 中
client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
让我们引入样板函数来开始与模型交互。如果你以前使用过Anthropic或其他模型,这会看起来很熟悉。
def chat_loop(user_query: str, conversation_history: List[Dict] = None) -> str:
if conversation_history is None:
messages = []
else:
messages = conversation_history.copy()
messages.append({"role": "user", "content": user_query})
while True:
response = client.messages.create(
model="claude-3-sonnet-20240229", # 请使用你可用的最新模型版本
max_tokens=1000,
messages=messages,
tools=tools
)
# 处理响应
for content_block in response.content:
if content_block.type == 'text':
# 如果有文本数据,将其添加到消息中
text_response = content_block.text
messages.append({"role": "assistant", "content": text_response})
# 如果这是最终响应,返回它
if response.stop_reason == 'end_turn':
return text_response, messages
# 否则,继续循环(模型可能在请求工具调用)
elif content_block.type == 'tool_use':
# 如果模型检测到需要使用工具
tool_name = content_block.name
tool_args = content_block.input
# 调用我们的辅助函数执行工具
tool_result = execute_tool(tool_name, tool_args)
# 将工具结果附加到消息列表中
messages.append({
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": content_block.id,
"content": str(tool_result)
}
]
})
# 继续循环,让模型处理工具结果并生成下一步响应
我们将从一个消息列表开始。我们将传入用户输入的查询。我们将与Claude 3 Sonnet对话。我们将为聊天启动一个循环。如果有文本数据,就将其放入消息中。如果传入的数据是工具调用,即模型检测到需要使用工具,我们将调用执行工具的辅助函数,然后将工具结果附加到消息列表中。
运行聊天机器人
让我们看看它的实际运行效果。引入我们的聊天循环,我们已经拥有了开始使用工具调用并与模型对话所需的所有功能。
现在,让我们从一个非常简单的例子开始,看看实际使用这个函数是什么样子。
我们将运行一个无限循环,直到输入字符串“quit”为止。
def main():
conversation_history = []
print("Chatbot started. Type 'quit' to exit.")
while True:
user_input = input("\nYou: ")
if user_input.lower() == 'quit':
print("Goodbye!")
break
response, conversation_history = chat_loop(user_input, conversation_history)
print(f"\nAssistant: {response}")
if __name__ == "__main__":
main()
现在,让我们开始与大型语言模型对话。调用我们的聊天循环函数。
现在,我们可以开始输入查询来与模型对话。让我们从一个非常简单的开始,比如打个招呼,确保它能按预期工作。
You: hi
我们可以看到,模型不仅会告诉我们它是一个AI助手,还会让我们知道它有哪些可用的工具。这很好。
使用工具进行查询
现在,让我们开始使用这些工具。让我们搜索关于感兴趣主题的最新论文。
我们为什么不搜索关于“algebra”的论文呢?这应该会使用我们已有的工具来搜索论文。
You: Search for recent papers on algebra.
我们可以看到主题被传入,结果被保存到这里。这很好。它甚至还会跟进问:“Would you like me to extract more detailed information?”
所以我会回答:“是的,请提取你找到的前两篇论文的信息并为我总结它们。”
我们将利用之前得到的工具结果,并传入它。它会告诉我们它对哪些ID感兴趣。所以我要确保传入那些特定的ID,以便正确完成。
ID在这里。我们将看到,它将使用这些特定的ID来提取信息。然后我们将得到结果以及这里的总结。
我们得到了一些关于不变代数和代数变形的信息。老实说,我无法告诉你这是什么。但希望我能阅读论文并了解这些信息。
需要记住的一点是,这里没有持久性记忆。所以,当你继续搜索查询并获取ID时,没有任何东西会被永久存储。因此,请确保在持续查询时,你传入了那些ID,并将每次对话都视为全新的开始。
如果你想退出这个聊天,记住,我们总是可以输入“quit”。确保你运行它。然后我们会看到我们在这里都完成了。
总结 📝
在本节课中,我们回顾了大型语言模型、工具调用以及arXiv SDK的使用。我们看到了如何构建一个能够搜索和总结学术论文的聊天机器人。接下来,我们将学习如何重构此代码,将这些工具转化为MCP工具,以便服务器能够将信息传递给我们。然后,我们将测试该服务器并查看结果。

我们下节课再见。
005:创建MCP服务器 🛠️



在本节课中,我们将学习如何将之前为聊天机器人实现的工具,封装成一个标准的MCP服务器。我们将使用Fast MCP库来简化构建过程,并利用MCP Inspector工具来测试我们的服务器。
概述
上一节我们介绍了如何将函数定义为工具并传递给大语言模型。本节中,我们将把这些工具的定义和模式抽象出来,创建一个独立的MCP服务器。我们将使用Fast MCP库,它提供了一个高级接口来快速构建MCP服务器。
开始编码
我们将从上一节课结束的地方开始,那里我们定义了两个函数:search_papers(用于在arXiv上查找论文)和extract_info。现在,我们将使用一个名为Fast MCP的库来创建MCP服务器。
首先,我们需要导入必要的模块并初始化MCP服务器。
from mcp.server.fastmcp import FastMCP
初始化MCP服务器并为其命名。
mcp = FastMCP("research")
模型上下文协议包含多个基本元素,例如工具、资源和提示。我们现在从定义一个工具开始。这可以通过使用@mcp.tool装饰器来轻松实现。
@mcp.tool()
def search_papers(query: str, max_results: int = 5):
# 函数实现代码...
我们需要为下面的函数也添加相同的装饰器。
@mcp.tool()
def extract_info(paper_id: str):
# 函数实现代码...
这样就在我们的MCP服务器上定义了两个可以运行和测试的工具。

最后,我们需要确保有正确的命令来启动这个服务器。我们将添加一段标准的Python代码。
if __name__ == "__main__":
mcp.run(transport="stdio")
这段代码允许我们直接运行此文件,而如果它被导入,则不会运行。我们通过调用mcp.run()并传入transport="stdio"来初始化和运行服务器。在本地运行服务器时,我们几乎总是使用标准输入/输出(stdio)传输方式。
执行以上代码后,我们将生成一个名为research_server.py的Python文件。这个文件将在我们启动MCP服务器时使用。
设置环境并测试服务器
接下来,我们需要设置环境并测试我们的服务器。我们将打开一个新的终端,并进入代码所在的目录。
以下是设置步骤:

- 使用
uv包管理器初始化项目并创建虚拟环境。 - 激活虚拟环境。
- 安装必要的依赖项(
mcp和arxiv)。
虚拟环境是一种将依赖项独立封装的方式,以避免与全局安装的包发生冲突。
安装完依赖项后,下一步是测试我们的服务器文件。我们将使用一个名为“Inspector”的浏览器工具来探索服务器提供的工具、资源和提示等基本元素。
在终端中运行以下命令来启动Inspector:
npx @modelcontextprotocol/inspector uv run research_server.py
这个命令会启动MCP Inspector,并在浏览器中打开它。在Inspector界面中,我们需要选择传输类型为“stdio”,并输入运行命令uv run research_server.py。
连接服务器后,我们可以看到可用的基本元素。我们目前只创建了一些工具。在Inspector的“Tools”部分,我们可以列出所有可用的工具,并运行它们进行测试。
例如,我们可以运行search_papers工具来搜索“chemistry”主题的论文。运行后,我们将得到返回的结果。同样,我们也可以测试extract_info工具。
MCP Inspector对于构建服务器或测试他人编写的服务器来说,是一个非常有价值的沙箱环境。

测试完成后,我们可以返回终端,使用Ctrl+C来停止服务器进程。如果需要再次启动,可以使用上箭头键调出之前的命令。


总结

本节课中,我们一起学习了如何将函数封装为MCP工具,并使用Fast MCP库快速构建一个MCP服务器。我们设置了Python虚拟环境来管理依赖,并利用MCP Inspector工具在浏览器中测试了服务器的功能。在下一节课中,我们将在此基础上添加MCP主机和客户端,并将这个MCP服务器集成到我们构建的聊天机器人中。
006:创建MCP客户端

概述
在本节课中,我们将学习如何创建一个MCP客户端,并将其集成到聊天机器人中。客户端将负责与MCP服务器通信,获取工具定义,并执行工具调用。我们将从底层代码开始,逐步构建一个功能完整的客户端。
从服务器到客户端
上一节我们介绍了如何使用MCP构建服务器。本节中,我们来看看如何创建一个MCP客户端,让聊天机器人能够与服务器通信,并访问工具定义和结果。
现在我们已经了解了如何用MCP构建服务器,接下来我们将超越检查器,构建自己的主机来容纳一个客户端,以便与我们的MCP服务器对话。

我们将直接处理聊天机器人。如果你想查看其他文件,比如我们之前制作的服务器,可以随时查看。我们将从回顾之前在聊天机器人示例中看到的代码开始。
你会再次看到很多这段代码,但随着我们开始引入客户端,我们将在此基础上增加一些内容。这里看到的所有内容我们之前都见过。这只是使用Claude 3 Sonnet以及工具使用来处理查询的能力。
我们可以看到,这里我们实际上没有定义任何工具,所有工具都在我们制作的服务器中定义。
创建MCP客户端
在上一课的基础上,我们现在开始讨论如何引入和创建MCP客户端。
我将从底层的MCP库中引入一些代码,因为我想解释这里实际发生了什么。请记住,当我们创建一个位于主机内部的MCP客户端时,我们需要确保该客户端与MCP服务器建立连接。
重要提示:我们正在查看的代码层级略低。你并不总是需要从头开始构建客户端,但当你看到其他工具(如Claude Desktop或Claude AI)时,了解其底层原理非常重要。因此,如果这段代码看起来有些复杂,请不要担心,我们将逐步讲解。这里的目的是确保你理解客户端是如何创建的,以及它们如何与服务器建立连接。
我们在这里看到的是从底层MCP库导入的一些必要类,用于建立与服务器的连接,以及从客户端启动子进程的能力。
以下是创建客户端连接的主要步骤:
-
建立服务器连接参数:首先,我们需要指定要连接的服务器及其必要参数。这看起来会非常熟悉,就是我们之前运行
uv run research_server.py的命令。我们在这里指定,让客户端知道如何启动服务器。如果需要任何环境变量,我们可以在这里传入。 -
启动服务器子进程:下一步是实际建立连接并将服务器作为子进程启动。由于我们可能不希望这成为阻塞操作,我们将在Python中大量使用
async和await。如果你不太熟悉这些,没关系,我会引导你完成需要做的事情。我们将定义一个名为run的函数,并设置一个上下文管理器。 -
建立会话与握手:一旦我们建立了要连接的服务器,我们将获得一个读写流,然后可以将其传递给一个更高级的类,即
ClientSession。当我们传递读写流到这个客户端会话时,我们将获得一个底层连接,允许我们使用列出工具、初始化连接等功能。 -
列出与使用工具:我们要做的第一件事是建立握手并初始化会话。然后,我们将列出服务器提供的所有可用工具。记住,客户端的职责是查询工具,获取这些工具,并将它们传递给大语言模型。我们将利用之前看到的聊天循环功能,如果需要调用某个工具,我们将让MCP服务器来完成这项工作。
-
执行工具:我们将看到一段略有不同的代码来执行底层工具。我们将从MCP服务器引入工具。如果需要执行某个工具,我们会通知MCP服务器该做什么。我们在上一课中已经定义了工具执行时所需的所有代码。
由于我们在异步环境中工作,我们将不再使用 mp.run,而是使用 asyncio.run。
集成客户端到聊天机器人
考虑到以上几点,让我们把所有内容整合起来。我们将把MCP客户端添加到我们的聊天机器人中。
我们将编写一个名为 mcp_chatbot.py 的文件,因为这是我们将在终端中运行以开始与聊天机器人交互的文件。
我们将引入之前看到的所有导入,以及 nest_asyncio,这是不同操作系统与Python事件循环正常配合所必需的。我们将引入我们拥有的任何环境变量,然后初始化我们的聊天机器人。
当我们初始化聊天机器人时,我们还没有当前的会话,也没有任何可用的工具。我们将看到,一旦开始建立连接,这些值就会改变。
我们的 process_query 函数看起来与上面非常相似,只是在需要调用工具时略有不同。我们使用已建立的会话返回到MCP服务器并执行必要的工具。
然后,我们将遵循类似的逻辑来追加消息并使用我们之前见过的工具调用。我们的聊天循环也将看起来非常相似。我们将持续运行,直到有人输入 quit,并在有数据输入时处理该特定查询。
为了总结,我们将定义一个名为 connect_to_server_and_run 的函数,它就像我们之前看到的那样执行:建立与MCP服务器的连接,获取读写流和底层会话,以便建立连接、列出我们需要的工具,然后获取这些工具并将它们传递给模型以进行工具使用。
最后,我们初始化聊天机器人并调用 connect_to_server_and_run 函数。在 if __name__ == "__main__": 中,我们使用 asyncio 运行主函数。

运行与测试
让我们运行这段代码来创建必要的 mcp_chatbot.py 文件。
打开终端,进入 L5 目录,然后 cd 进入 mcp_project 文件夹。查看当前内容,我已经有一个存在的虚拟环境。所以,我将激活那个虚拟环境:source .venv/bin/activate。
我们还需要一些其他依赖项才能使这个项目工作。所以,我将添加Anthropic SDK、用于环境变量访问的 python-dotenv 模块以及 nest_asyncio。
添加这些依赖项后,我应该拥有启动聊天机器人所需的一切。在启动聊天机器人之前,让我们确保了解这是如何组合在一起的。
当我输入 uv run mcp_chatbot.py 时,我们将连接到我们的MCP服务器,使用定义的工具,将这些工具传递给Claude,然后创建一个良好的界面,让我们开始与Claude对话,以访问这些工具和我们想要的任何其他数据。
我们可以看到,当建立连接时,我们正在处理 list_tools 请求。这是协议中允许我引入必要工具的底层功能。

我们已经连接到服务器,并获得了以下工具,现在可以开始与聊天机器人对话了。我们总是可以从一些简单的事情开始,以确保一切正常,比如向聊天机器人发送一个友好的问候查询。

现在,让我们继续使用我们拥有的一些工具。例如,我可以问:“你能搜索关于物理的论文吗?并只为我找到其中的两篇。”

我们将在这里使用那些特定的工具。我们将看到,通过 call_tool 请求,MCP客户端正在将这些数据发送到服务器。服务器正在调用该工具并将其返回给我们。然后,我们使用Claude和额外的上下文来返回一个很好的摘要给我们。

总结
本节课中,我们一起学习了如何构建一个MCP客户端。虽然我们做了一些底层的编程工作来实现这个功能,但我们已经为构建极其强大的应用奠定了基础。
我们首先建立了客户端会话,使其能够与MCP服务器通信并利用其工具。接下来,我们将建立多个客户端会话,以允许使用许多不同的MCP服务器,这样它们就可以开始协同工作。然后,我们将开始添加其他原语,如资源和提示,以真正看到这项工作在更大规模上的应用。

下节课再见。别忘了,如果你想退出聊天机器人,随时可以输入 quit。
007:连接MCP聊天机器人到参考服务器




概述
在本节课中,我们将学习如何升级我们的MCP聊天机器人,使其能够连接到多个不同的服务器,而不仅仅是之前我们自建的那一个。我们将了解Anthropic团队开发的参考服务器,学习如何下载和使用它们,并通过一个配置文件来动态管理多个服务器连接。
在上一节中,我们成功将聊天机器人连接到了自己构建的一个服务器。本节中,我们将看看如何让它连接到任意服务器。
探索Anthropic参考服务器
首先,我们来了解一下Anthropic在官方仓库中提供的一些参考服务器。这些服务器由Anthropic团队开发和维护,可以作为我们构建自己应用的强大基础。
访问Github上的模型上下文协议仓库,你会发现一个非常庞大的服务器列表。我们主要关注其中的“参考服务器”。除了这些,还有许多第三方服务器和官方集成。可以说,目前你能想到的任何数据源,几乎都已经有了对应的MCP服务器。
我们不需要手动下载这些服务器并在本地运行。我们将学习如何通过简单的命令直接运行它们。本节课我们将重点使用两个参考服务器:fetch服务器和file system服务器。
以下是这两个服务器的简要介绍:
- Fetch服务器:这个服务器允许我们从网页抓取内容,并将HTML转换为Markdown格式,以便大语言模型更好地处理。它使用Python编写。
- 文件系统服务器:这个服务器允许我们访问本地文件系统,进行读取、写入文件,搜索文件,获取元数据等操作。它使用TypeScript编写。
配置服务器连接
每个参考服务器都需要一些配置,比如服务器名称、运行命令等。为了灵活地管理多个服务器,我们将不再在代码中硬编码这些参数,而是创建一个JSON配置文件。
我们将同时连接三个服务器:我们自建的research服务器、fetch服务器和file system服务器。通过这个配置文件,我们可以轻松地指定如何与每个服务器交互。
以下是我们将创建的servers.json配置文件示例:
[
{
"name": "research",
"command": "uv",
"args": ["run", "research"]
},
{
"name": "fetch",
"command": "uvx",
"args": ["mcp-server-fetch"]
},
{
"name": "filesystem",
"command": "npx",
"args": ["-y", "model-context-protocol-server-filesystem", "."]
}
]
对于文件系统服务器,我们通过参数.指定了允许访问的路径为当前目录,这意味着它无法读取或写入此目录之外的文件。
升级MCP聊天机器人代码
现在,让我们看看如何更新MCP聊天机器人的代码,以支持读取JSON配置文件并连接到多个服务器。
代码的核心变化在于需要管理多个连接会话,并将每个工具正确地映射到其所属的会话。虽然这里的代码并非生产就绪,但它清晰地展示了像Claude Desktop、Cursor、Windsurf这类工具在底层是如何建立多服务器连接的。
主要更新包括:
- 维护会话和工具列表:我们需要维护所有已连接会话的列表,以及每个工具对应的会话信息。
- 异步连接管理:由于在异步环境中管理多个上下文管理器,我们使用
AsyncExitStack来管理读写连接和整个会话连接。 - 读取配置并连接:代码会读取
servers.json文件,解析为字典,然后遍历并为每个MCP服务器建立连接。 - 整合聊天循环:主聊天循环的逻辑与之前类似,但在需要调用工具时,会去查找并调用特定会话中的对应工具。在结束时,会清理所有服务器的连接。
主函数现在负责连接所有配置的服务器,然后启动聊天循环。
实践演示
让我们在终端中运行更新后的聊天机器人,看看多个服务器如何协同工作。

首先,进入项目目录并激活虚拟环境:
cd mcp_project
.venv\Scripts\activate # Windows
# 或 source .venv/bin/activate # Linux/Mac
然后运行聊天机器人:
uv run mcp_chatbot.py
程序启动后,我们可以看到它成功连接了文件系统服务器、研究服务器和抓取服务器,并列出了所有可用的工具。


现在,让我们尝试一个复杂的提示,综合利用这三个服务器的能力:
“请抓取模型上下文协议(Model Context Protocol)网站的内容,将摘要保存到名为
mcp_summary.md的文件中,并创建一个总结该内容的可视化图表。”

机器人会执行以下步骤:
- 使用
fetch工具获取网页内容。 - 处理并总结内容。
- 使用文件系统工具将摘要写入
mcp_summary.md文件。 - 生成一个描述图表的文本(未来可集成绘图工具生成图像)。




我们再试一个结合所有三个服务器的例子:
“抓取‘deeplearning.ai’网站,从中找到一个有趣的术语,围绕这个术语搜索相关论文,然后将你的发现总结并写入名为
results.txt的文件。”


这个过程展示了如何串联使用多个工具:抓取网页、提取信息、研究论文、最后输出结果。


总结
本节课中,我们一起学习了如何将MCP聊天机器人升级为能够连接和管理多个服务器的强大客户端。关键点包括:
- 了解了Anthropic的参考服务器生态。
- 学会了通过
uvx和npx命令直接运行远程服务器。 - 掌握了使用JSON配置文件来灵活管理服务器连接的方法。
- 看到了
fetch、file system和我们自建的research服务器如何协同工作,完成复杂的多步骤任务。

现在,任何现有的MCP服务器都可以通过最小化的配置添加到你的应用中。你可以组合不同服务器的结果,为像Claude这样的模型提供连接外部世界所需的所有上下文。在下一节课中,我们将开始添加其他原语,例如用于只读数据的“资源”和用于生成预制提示的“提示模板”。
008:添加提示词与资源功能


概述
在本节课中,我们将学习如何为MCP服务器添加两个新功能:资源和提示词模板。我们将看到如何在服务器端定义它们,以及如何在客户端(聊天机器人)中消费和使用这些功能,从而为用户提供更丰富、更便捷的交互体验。
从工具到资源与提示词
上一节我们介绍了如何创建连接多个MCP服务器的客户端。现在,让我们回到协议中的其他核心概念,比如资源和提示词,并探讨如何在服务器端添加它们,以及如何让客户端具备消费这些数据的能力。
在我们的研究服务器代码中,你可以找到所有相关文件。接下来,我们将一起浏览服务器端和客户端的一些关键代码。
正如之前所见,添加一个工具非常简单,只需使用 @mcp.tool 装饰器。现在,让我们引入资源和提示词,并讨论如何添加它们。
在服务器端添加资源
当前代码运行在我们的服务器上。我们将为特定的文件夹以及特定主题的论文引入一些资源。
请记住,资源是只读数据,应用程序可以选择使用它,或者将其提供给模型。我们不是创建工具去文件系统中获取数据(就像用HTTP的GET请求获取数据一样),而是对资源做同样的事情。
在服务器端,我们定义了两个资源:
- 一个URI为
papers://folders的资源,用于列出论文目录中可用的文件夹。 - 一个用于获取特定主题信息的资源。
目前,我们尚未实现这些资源的具体呈现方式或获取逻辑。在服务器端,我们只是设置了监听这些特定资源请求的方法。
代码中包含了一些字符串操作和文件读取逻辑,以获取必要的数据,并附带错误处理,确保在找不到论文时返回错误信息。我们可以看到,代码正在从 papers_info.json 文件中读取数据,然后返回包含相关内容的文本。
服务器端资源定义示例(概念):
@mcp.resource(uri="papers://folders")
def list_folders():
# 读取目录并返回文件夹列表
...
@mcp.resource(uri="papers://topic/{topic_name}")
def get_papers_by_topic(topic_name: str):
# 根据主题读取JSON文件并返回论文信息
...
在服务器端添加提示词模板
除了资源,我们还可以向MCP服务器添加提示词或提示词模板。
在深入代码之前,让我们回顾一下提示词模板这个原语的目的。提示词旨在由用户控制。作为AI应用的用户,你不需要自己进行复杂的提示词工程。实际上,你可能正在使用一个服务器,试图获取一些信息,但根据你已有的提示,你可能不知道获取或检索信息的最佳方式。
提示词模板在服务器上创建并发送给客户端,以便用户可以使用这些完整的模板,而无需自己进行所有的提示词工程。因此,我们不是要求用户指定如何搜索论文,而是将提供一个经过实战检验的提示词模板,其中包含他们可以填入的动态信息,比如主题或论文数量。你可以想象,我们可以进行一些相当复杂的评估和提示词工程测试,而到达用户手中时,这一切都被抽象化了。
我们通过使用 @mcp.prompt 装饰一个函数来创建提示词模板,然后返回该模板的样子。在客户端,我们只需要让用户输入论文数量(可选)和主题(必需)。
服务器端提示词模板定义示例(概念):
@mcp.prompt()
def generate_search_prompt(topic: str, num_papers: int = 5):
return f"""
请搜索关于 {topic} 的学术论文。
请返回 {num_papers} 篇最相关的论文摘要。
"""
在客户端消费资源与提示词
现在我们已经了解了服务器将发送什么给客户端,接下来需要弄清楚如何开始引入这些资源和提示词,以及如何为它们创建用户界面。
我们创建的UI和呈现方式完全由开发者决定。MCP的强大之处在于,它并不强制所有界面看起来和工作方式都一样。它只专注于发送和操作数据,而呈现方式则由客户端和宿主应用来创建。
考虑到这一点,让我们回到聊天机器人代码。和之前一样,这里会有一些稍微底层的代码。幸运的是,它与我们之前看到的代码相对类似。
我们将存储可用工具和提示词的列表,以及所有特定资源的URI。在我们的 connect_to_server 函数中,代码看起来和之前很相似。我们将使用 ExitStack 在异步环境中管理所有连接,初始化会话,然后不仅仅是获取工具,对提示词和资源也做同样的事情:使用为每个客户端建立的会话来列出提示词、工具和资源。如果服务器不提供提示词或资源,我们会处理错误并打印异常。连接服务器时出现的任何问题也会被处理。
connect_to_servers 函数看起来和之前相似,读取JSON文件并加载所有服务器名称和必要配置。process_query 函数也相对类似,我们将创建一个包含可用工具的消息,如果使用工具调用,就追加该信息,并确保调用正确的工具。
代码开始有所不同之处在于处理资源和提示词模板的部分。
处理资源:
要获取单个资源,我们需要确保处理正确的URI。一旦有了正确的URI,我们就从该URI读取资源。在这里,我们只是简单地打印出该特定资源的内容。但根据你想要构建的界面,你可以对这些数据做任何你想做的事情。
处理提示词:
我们将列出所有可用的提示词。如果这些提示词需要任何参数,我们会将其展示给用户。当有提示词输入时,我们将执行它。对于我们所处的特定会话,我们获取该提示词,然后用查询来执行它。执行提示词的函数需要获取提示词名称及其可能需要的任何参数。一旦获取到特定提示词,我们就将其作为消息内容传入,并使用这些参数处理查询。
聊天循环的不同之处:
聊天循环是我们开始添加特定用户界面的地方,用于获取资源和提示词。这里进行了一些字符串操作,这完全取决于作为宿主和客户端开发者的你,希望如何呈现内容。
- 我们将使用
@符号来获取特定资源。如果我们看到首先传入了一个主题,就使用该URI来获取它。 - 如果我们看到查询以
/开头,这表示我们正在使用一个特定的提示词。 - 如果命令是
/prompts,我们将向用户展示所有提示词。 - 如果命令是
/prompt,我们将确保传入参数。为了传入参数,我们同样进行了一些字符串操作,寻找由等号分隔的键值对。一旦获得所需信息,就执行提示词。

我们还有与之前相似的清理逻辑以及连接聊天机器人的逻辑。
实战演示
代码部分很多,让我们退一步,在终端中实际查看效果。
进入项目目录并激活虚拟环境后,运行我们的聊天机器人:
python mcp_chatbot.py
我们将看到,这里连接到了许多不同的MCP服务器。代码中包含了一些错误处理,以防这些服务器不提供工具、资源或提示词。
我们不仅可以看到进行查询并与大语言模型对话的能力,还可以访问我们拥有的资源。例如,查看可用的文件夹,代码会读取特定URI的资源,并显示一个名为“computers”的文件夹(这是之前搜索的结果)。我们可以访问这些论文,相关信息会直接显示出来。
现在,我们无需编写工具来获取数据,也无需模型完成所有工作,而是可以直接向模型提供这个上下文。如果模型选择将其添加到上下文窗口中,并且应用程序需要,我们就可以利用它。

让我们看看可用的提示词。服务器上创建了一个名为 generate_search_prompt 的提示词,我们还可以看到 fetch 服务器也提供了一个用于获取URL内容并将其提取为Markdown的提示词,其参数是URL。
使用提示词:
使用方法是添加 /prompt 命令。我们会看到用法要求提供提示词名称以及任何必需的参数。让我们使用我们的 generate_search prompt:
/prompt name=generate_search_prompt topic=math
num_papers 参数是可选的,所以我可以传入一个数字,或者默认为5。使用这个带有我定义的动态变量 topic 的提示词后,我们将看到代码处理该提示词,生成必要的文本并执行它。
这个过程看起来很熟悉:与 arxiv 对话以获取特定论文,然后将这些论文添加到我们为“math”创建的文件夹中。完成后,我也应该能够通过资源访问这些数据。请记住,随着应用程序中数据的更改,这些资源是动态更新的。
查询完成后,我们可以看到模型给我的回复。查看我们的文件夹,现在可以看到“computers”和“math”主题。如果想访问该文件,可以查看其中的内容。这样,我们就结合使用了提示词和资源。


总结
本节课我们完成了不少内容。我们探索了如何在服务器上添加提示词和资源,然后在聊天应用中消费它们。我们将工具、资源和提示词等核心原语结合起来,并连接了多个MCP服务器。

在下一课中,我们将开始为更强大的界面引入其他类型的宿主应用,但会用到我们之前学到的许多概念。
009:为Claude Desktop配置服务器 🖥️
在本节课中,我们将学习如何将我们构建的MCP服务器与Claude Desktop这类MCP兼容的应用程序连接起来。你将看到如何通过简单的配置,让Claude Desktop自动连接并管理MCP服务器,从而省去编写底层网络代码的麻烦。
概述
上一节我们介绍了如何构建自己的MCP服务器和客户端。本节中,我们来看看如何将服务器集成到像Claude Desktop这样的现成应用程序中。通过这种方式,我们可以利用这些应用程序的界面来使用服务器的提示词、资源和工具,而无需处理复杂的客户端代码。
配置Claude Desktop连接MCP服务器
首先,确保你拥有之前课程中构建的研究服务器。我们将在一个项目文件夹中设置环境并安装依赖,然后在Claude Desktop中配置服务器连接。
以下是配置步骤:
- 初始化环境与安装依赖:在项目文件夹中,创建并激活虚拟环境,然后安装必要的依赖包。
python -m venv venv source venv/bin/activate # Windows系统使用 `venv\Scripts\activate` pip install anthropic mcp

- 配置Claude Desktop:打开Claude Desktop,进入设置(Settings)的开发者(Develop)选项,编辑MCP配置文件。
![]()

-
编辑配置文件:在打开的JSON配置文件中,粘贴服务器的配置信息。关键点在于,需要指定研究服务器的确切文件路径,以便Claude Desktop启动它。
![]()
配置文件示例结构如下:{ "mcpServers": { "research-server": { "command": "python", "args": ["/完整/路径/到/你的/research_server.py"] } } }这个过程抽象了底层的子进程连接和网络通信,我们无需再手动编写这些代码。
-
重启应用:修改配置文件后,需要关闭并重新启动Claude Desktop以建立连接。
探索MCP兼容的应用程序生态系统
成功重启后,你可以在Claude Desktop的界面中访问已连接服务器提供的工具、资源和提示词。


Claude Desktop只是众多支持模型上下文协议(MCP)的应用程序之一。在MCP官方文档中,你可以找到一个不断增长的支持MCP集成的应用程序列表。
以下是主要的应用程序类别:
- Web应用与代理程序
- 命令行界面(CLI)工具
- 集成开发环境(IDE)
这些应用程序都支持MCP的核心原语,如资源、提示词和工具。你可以点击其中任何一个,了解如何在这些现成的应用环境中与MCP服务器交互。其强大之处在于,虽然这个生态系统看起来很庞大,但通过构建自己的服务器,你已经理解了其底层的工作原理。
实践:组合多服务器功能
现在,让我们将所有这些功能结合起来,进行一次实践。我们将使用来自不同MCP服务器的工具组合来完成一个任务。
我们将执行以下操作:
- 使用 fetch工具 访问DeepLearning.AI网站,寻找一个有趣的机器学习主题。
- 使用 研究服务器 的
search_papers工具,查找并总结与该主题相关的几篇论文。 - 利用Claude Desktop的 “工件”(artifacts) 功能,基于论文中的关键主题,生成一个带有闪卡集的Web测验应用。
这个过程展示了如何组合不同服务器的工具来获取所需数据,并利用应用程序的高级功能(如可视化)构建强大的跨领域应用。



虽然这只是一个简单的例子,但它启发了无数潜在用例,从摘要生成到交互式应用开发。
总结
本节课中,我们一起学习了如何使用像Claude Desktop这样的工具来连接MCP服务器,从而抽象掉大量的底层网络和编码工作。我们还浏览了众多不同的MCP兼容应用程序,它们可以帮助你在各种IDE、Web应用和桌面客户端中快速启动和运行。



在下一节中,我们将更深入地探讨如何构建远程MCP服务器。


我们下节课再见。

010:创建与部署远程服务器 🚀


在本节课中,我们将学习如何构建、测试并部署一个远程MCP服务器。我们将了解如何将本地运行的服务器转换为可通过网络访问的远程服务,并使用云平台进行部署。
上一节我们介绍了如何在本地构建和连接MCP服务器。本节中,我们来看看如何让服务器在远程运行,以便任何地方的客户端都能连接。

构建远程服务器

将本地服务器转换为远程服务器,其核心代码逻辑无需大幅改动。主要区别在于底层的通信传输方式。
以下是远程服务器的核心配置,关键在于指定了SSE传输方式:
# 在服务器代码底部指定传输方式
# 当前SDK暂不支持HTTP流式传输,因此使用SSE
transport = SSETransport()
服务器中的工具、资源和提示词等核心功能保持不变。

测试远程连接
服务器配置完成后,我们需要测试远程连接是否正常。
以下是使用MCP检查器连接远程服务器的步骤:
- 启动检查器。
- 在检查器界面中,确保代理地址正确。
- 将传输类型设置为SSE。
- 输入远程服务器的SSE连接URL。
- 点击连接。
连接成功后,检查器将显示服务器提供的资源、提示词和工具列表,证明远程连接已建立。
部署到云平台

测试通过后,我们可以将服务器部署到云平台,使其公开可用。这里我们使用Render平台进行演示。

部署前需要将代码推送到GitHub仓库,以便Render拉取。

以下是准备代码仓库的步骤:
- 初始化Git仓库:在项目目录中执行
git init。 - 创建.gitignore文件:排除虚拟环境等不需要提交的文件夹,例如添加
venv/。 - 转换依赖管理:由于Render暂不支持Uv,需将依赖转换为Pip可识别的格式。使用命令
uv pip compile pyproject.toml > requirements.txt生成requirements.txt文件。 - 指定Python版本:创建
runtime.txt文件,并写入python-3.11.11以指定运行时版本。 - 提交代码:执行
git add .和git commit -m "ready for deployment"。 - 关联远程仓库:在GitHub创建新仓库,并按照提示将本地仓库推送到远程。
代码推送到GitHub后,即可在Render平台进行部署。

以下是Render部署流程:
- 登录Render控制台,点击“New Web Service”。
- 连接你的GitHub账户,并选择刚创建的代码仓库。
- 在部署设置中,将启动命令修改为
python research_server.py。 - 选择免费计划,然后开始部署。
部署过程需要几分钟。完成后,访问服务根URL可能会看到404错误,这是正常的。访问SSE端点(例如 your-service.onrender.com/sse)并看到返回的会话ID,即表示服务器已成功部署并运行。



本节课中我们一起学习了如何将MCP服务器从本地运行扩展到远程部署。我们了解了修改传输配置、测试远程连接,以及通过GitHub和Render平台完成自动化部署的全过程。现在,你的AI应用已经可以连接到任何地方的远程MCP服务器了。
011:总结与展望 🎯
在本节课中,我们将回顾整个课程的核心内容,并探讨模型上下文协议(MCP)的未来发展方向,包括一些尚未深入介绍的高级功能与即将到来的新特性。
课程回顾
在之前的课程中,我们学习了MCP的核心概念。我们构建了一个能够暴露工具、资源和提示的服务器,开发了一个聊天机器人并连接到多个服务器,使用云桌面构建了更复杂的应用程序,并部署了自己的远程服务器。
MCP协议本身也在不断发展。在这最后一课中,我们将了解MCP的其他功能,以及协议即将推出的一些令人兴奋的新特性。


协议高级功能
我们已经学习了关于模型上下文协议的许多知识,包括主机、客户端、服务器、工具、资源和提示。我们也有机会编写代码,利用所有这些概念来驱动更大的应用程序。然而,关于模型上下文协议,仍有一些内容我们尚未涉及。其中许多功能正处于积极开发阶段,您始终可以通过Github上的规范说明和相关讨论来了解最新进展。


认证机制 🔐
我们尚未涉及的第一部分是模型上下文协议中的认证。在三月份的规范更新中,OAuth 2.1 被添加为与远程服务器进行认证的手段。这使得客户端和服务器能够进行身份验证,并向数据源发送经过身份验证的请求。您可以想象,许多不同的服务器需要访问需要某种形式认证的数据。这需要客户端向服务器发出请求,然后服务器要求某个用户进行身份验证。一旦认证过程成功完成,客户端和服务器就可以交换令牌,客户端可以向服务器(进而向数据源)发出经过身份验证的请求。协议的这一部分正在积极开发中,总是有新的功能和安全措施被添加进来。但认证将主要通过 OAuth 2.1 协议完成。需要强调的是,这是模型上下文协议的一个可选功能,但对于具有标准I/O的远程服务器,强烈推荐使用。我们使用环境变量,不需要这种认证。这是建立在既定标准之上的,您可以通过下面的链接查看。
客户端原语

在探索了服务器可以暴露的原语(工具、资源和提示)之后,我们还有客户端可以暴露的原语。这些包括路由和采样。让我们深入了解它们。

路由是客户端建议服务器应在其中操作的URI。其核心思想是,当客户端连接到服务器时,只查看特定文件夹中您可能需要的文件。它可以声明服务器应使用的路由。这对于文件系统路径很有用,但也可以是任何有效的URI,包括HTTP URL。路由的好处在于允许安全限制,使服务器专注于相关的文件路径或位置。路由也具有一些内置的通用性,它们对文件路径很有用,但也可以是任何有效的HTTP URI。我们正慢慢看到越来越多的客户端采用这个原语,这是一个随着协议发展需要关注的重要特性。
采样功能

采样允许服务器向大型语言模型请求推理。这有点像通信的另一面,不是客户端与大型语言模型对话,而是服务器可以反过来与客户端对话并请求推理。一个例子可能是用户报告网站因某种原因变慢的情况。您的MCP服务器可以收集服务器日志、性能指标、错误日志,并与各种数据源通信以了解情况。服务器不是将这些数据返回给客户端(将所有内容放入上下文窗口,或可能导致服务器与客户端之间的任何安全漏洞),而是可以直接与大型语言模型对话,要求其诊断性能问题。大型语言模型分析模式并返回数据,然后服务器可以生成步骤以使网站不那么慢。当从安全角度存在担忧,或可能违反边界,或者您可能不希望所有数据返回后被放入上下文时,采样和创建采样循环是服务器请求推理的一种非常强大的方式,它切换了我们之前看到的通信方向。
当我们开始探索模型上下文协议的智能体能力时,这也非常强大。随着我们迈向更多模型与不同数据源对话的世界,并赋予模型更多自主权来调用不同的工具并自行行动,我们相信MCP将成为智能体的基础协议。

多智能体架构与未来路线图
当我们开始思考如何将MCP用于智能体能力时,您可以想象一个场景,用户和大型语言模型需要访问各种MCP服务器。模型上下文协议的强大之处在于它具有可组合性和某种递归性,客户端可以是服务器,服务器也可以是客户端。这使我们能够开始创建一种架构,利用客户端与服务器通信的能力,同时也让服务器能够通过采样向客户端请求所需的数据。我们在这里建立的是多智能体架构的概念,其中应用程序和大型语言模型与一个智能体通信。这个智能体恰好是一个MCP客户端和服务器,它可以将数据提供给应用程序,但也可以通过模型上下文协议连接到其他客户端和服务器。您可以想象,我们有用于分析、编码、研究的智能体,它们也恰好是MCP服务器。如果它们需要通过这种可组合的性质连接到其他服务器或客户端,我们可以开始思考允许多个智能体使用相同协议、说同一种语言的架构。


模型上下文协议路线图上的下一个重要部分是统一注册表的概念。其目的是标准化我们发现服务器本身的方式。正如我们之前所见,开源社区对此非常兴奋,有许多针对不同数据提供商(如Google Drive、Github等)的服务器,可能有数十个MCP服务器。但就像NPM或PyPI上的软件包一样,这些特定服务器内部可能存在恶意代码。因此,注册表API的用途在于发现服务器、集中管理这些服务器的位置,并验证这些服务器是否已得到社区和公司本身的信任。这也允许对特定的MCP服务器进行版本控制,锁定依赖关系,就像您在应用程序中所做的那样。令人兴奋的是,MCP服务器能够让智能体自行发现它们。您可以想象一个场景,用户需要根据某些日志中的内容修复一个错误。然后,智能体通过注册表API搜索官方的MCP服务器,安装它,查询并在这种特定用例中建议修复。这样,不需要应用程序从一开始就连接到各种服务器,我们可以开始构建能够动态发现和连接MCP服务器的应用程序。当我们结合认证来考虑这一点时,我们可以想象用户有一个请求,需要发现一个服务器(类似于其他协议,如OAuth和Google最近宣布的智能体到智能体协议),将Json文件放在一个众所周知的文件夹中的想法以前已经做过。但在这里,在这个MCP Json中,我们指定要连接的服务器的端点、它暴露的能力或原语,以及所需的认证。因此,用户可能会问:“帮助我管理我在Shopify上的商店。”智能体或AI应用程序将查看Shopify是否有一个众所周知的MCP Json文件。如果有,它将找出要连接哪个端点以及需要什么认证。一旦用户通过身份验证,智能体就可以通过注册表API执行必要的操作。我们可以允许这种动态发现的想法,并通过叠加OAuth 2.1,我们可以确保这些连接是安全的。
协议的未来发展
正如您可能在建议和讨论中看到的,协议还有更多内容即将推出。随着越来越多的客户端支持HTTP流式传输,目标是在有状态和无状态能力之间实现平滑过渡。随着远程MCP服务器的持续开发,扩展生态系统以支持更多此类服务器非常重要。您可以想象,当使用多个MCP服务器时,工具很可能出现命名冲突。您可以想象,服务器可能具有通用名称的工具,如“fetch_users”、“fetch_entities”,模型可能会对需要获取什么感到困惑。因此,思考如何防止冲突并创建服务器或工具的逻辑分组非常重要。我们谈到了采样或主动请求上下文。协议和我们进行的对话中投入了大量工作,以使采样等原语变得更加流行。最后,虽然OAuth 2.1对规范来说相对较新,但在大规模认证和授权方面仍有更多需要考虑。

总结
在短短的时间内,您已经了解了关于模型上下文协议的许多内容。您从概念上学习了原语,构建了服务器、客户端和主机,并看到了如何部署远程MCP服务器。仍有更多内容有待发现。因此,我鼓励大家查看相关的讨论和对话,并尽可能多地继续构建和研究。

非常感谢您加入我的这次旅程,我迫不及待想看到您用MCP构建出什么。🚀


浙公网安备 33010602011771号