欢迎学习本课
这门课会带你用 Python 从零构建 MCP 的服务器和客户端。学完你能让 Claude 安全地连接到外部服务和数据,而不必为每个集成手写一大堆代码。
你会学到什么
- MCP 的整体架构与通信方式
- 用 Python SDK 构建 MCP 服务器
- 三大核心原语:工具(tools)、资源(resources)、提示(prompts)
- 实现一个 MCP 客户端,把能力接给 Claude
Introduction to MCP
这门课带你用 Python SDK 从零构建 MCP(Model Context Protocol)的服务器和客户端,掌握三大核心原语——工具、资源、提示,让 Claude 安全地连接外部服务和数据。
在互动学院中学习(含测验与进度保存)→这门课会带你用 Python 从零构建 MCP 的服务器和客户端。学完你能让 Claude 安全地连接到外部服务和数据,而不必为每个集成手写一大堆代码。
MCP(模型上下文协议)是一套开放标准,让 AI 应用能以统一的方式连接到各种外部工具和数据源——就像给 AI 装了一个「通用接口」。
没有 MCP 时,每接一个工具都要单独写一套集成代码。MCP 把「定义工具、执行工具」的工作从你的应用转移到专门的 MCP 服务器上:你只要让应用支持 MCP,就能即插即用地接入任何 MCP 服务器。
MCP 是一套开放标准,把工具的定义和执行交给专门的 MCP 服务器,让 AI 应用能以统一方式即插即用地接入外部能力。
MCP 采用客户端-服务器架构。客户端嵌在 AI 应用里,负责和 MCP 服务器通信,把服务器提供的能力交给 Claude 使用。
用户提问 → MCP 客户端把可用的工具告诉 Claude → Claude 决定调用某个工具 → 客户端转发给 MCP 服务器执行 → 结果返回给 Claude → Claude 生成最终回答。整个过程你不用手写集成逻辑。
客户端嵌在 AI 应用中,负责发现服务器能力、把工具调用转发给服务器执行,并把结果交回给模型。
工欲善其事,必先利其器。本节带你完成 MCP 服务器项目的完整搭建,从安装依赖到第一次成功启动,打下坚实基础。
使用 pip 或 uv 安装 Python 版 MCP SDK。推荐使用 uv,它速度更快且自带虚拟环境管理。
# 方式一:使用 pip pip install mcp # 方式二:使用 uv(推荐) uv init my-mcp-server cd my-mcp-server uv add mcp
一个最小化的 MCP 服务器项目只需要一个入口文件。推荐的目录结构如下:
# server.py 最小起步代码
from mcp.server.fastmcp import FastMCP
mcp = FastMCP('my-server')
# 稍后在这里添加 tools、resources、prompts
if __name__ == '__main__':
mcp.run()使用 `mcp dev` 命令以开发模式运行服务器,它会自动启动 MCP Inspector 界面,方便你测试。
# 开发模式启动(自动打开 Inspector) mcp dev server.py # 或使用 uv run uv run mcp dev server.py
`mcp dev server.py` 是官方提供的开发模式启动命令,会自动启动 Inspector 界面。`python server.py` 只会直接运行,不会启动 Inspector。
Tools 是 MCP 的核心原语之一,由模型决定何时调用。本节你将学会用 `@mcp.tool()` 装饰器定义工具,并理解如何让模型"读懂"你的工具。
使用 `@mcp.tool()` 装饰一个 Python 函数,FastMCP 会自动将函数名、文档字符串(docstring)和类型注解注册为工具的元数据。模型依赖这些信息决定是否调用工具。
以下示例定义了两个工具:一个做数学运算,一个读取文件内容。注意类型注解和 docstring 的写法。
from mcp.server.fastmcp import FastMCP
mcp = FastMCP('my-server')
@mcp.tool()
def add_numbers(a: int, b: int) -> int:
'''将两个整数相加并返回结果。'''
return a + b
@mcp.tool()
def read_file(path: str) -> str:
'''读取指定路径的文件内容并以字符串形式返回。
Args:
path: 要读取的文件的绝对路径
'''
with open(path, 'r', encoding='utf-8') as f:
return f.read()
if __name__ == '__main__':
mcp.run()FastMCP 支持常见的 Python 类型注解,包括 str、int、float、bool、list、dict 以及 Pydantic 模型。返回值可以是字符串、数字或复杂对象,FastMCP 会自动序列化。
docstring 会作为工具描述传递给语言模型,模型依靠它来判断何时应该调用这个工具。这是工具设计中最重要的部分之一。
MCP Inspector 是官方提供的可视化调试工具,让你无需编写客户端代码就能测试服务器。本节带你掌握 Inspector 的核心用法。
运行 `mcp dev server.py` 即可同时启动服务器和 Inspector。Inspector 是一个运行在浏览器中的 Web UI,默认监听 5173 端口。
# 启动服务器并打开 Inspector mcp dev server.py # 终端输出示例: # MCP Inspector is up and running at http://localhost:5173 # Starting MCP server my-server...
Inspector 界面左侧列出所有已注册的工具、资源和 Prompts。点击工具后可以在右侧填入参数并手动触发调用,无需任何额外代码。
Inspector 底部会展示原始的 JSON-RPC 消息,这对理解 MCP 协议非常有帮助。一个典型的工具调用请求和响应如下:
// 请求(Inspector 发送给服务器)
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "add_numbers",
"arguments": { "a": 3, "b": 5 }
}
}
// 响应(服务器返回)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [{ "type": "text", "text": "8" }]
}
}Inspector 提供了一个可视化的 Web UI,让开发者可以直接调用服务器工具并查看请求/响应,无需先实现客户端代码。
服务器写好了,现在轮到客户端登场。本节你将用 Python 实现一个真正能与 MCP 服务器通信的客户端,亲身体验完整的请求—响应流程。
MCP Python SDK 提供了 `ClientSession` 类来管理与服务器的连接。客户端通过 `StdioServerParameters` 告诉 SDK 如何启动并连接到服务器进程。
以下是一个完整的 Python 客户端,连接到本地服务器、列出工具、并调用 add_numbers 工具:
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
# 描述如何启动服务器
server_params = StdioServerParameters(
command='python',
args=['server.py']
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
# 初始化连接
await session.initialize()
# 列出所有工具
tools = await session.list_tools()
print('可用工具:')
for tool in tools.tools:
print(f' - {tool.name}: {tool.description}')
# 调用工具
result = await session.call_tool('add_numbers', {'a': 10, 'b': 32})
print(f'\n调用结果:{result.content[0].text}')
if __name__ == '__main__':
asyncio.run(main())确保 server.py 在同一目录下,然后直接运行 client.py。SDK 会自动启动服务器进程、建立 STDIO 连接,完成后自动清理。
python client.py # 预期输出: # 可用工具: # - add_numbers: 将两个整数相加并返回结果。 # - read_file: 读取指定路径的文件内容... # # 调用结果:42
StdioServerParameters 告诉客户端 SDK 用什么命令和参数来启动服务器进程,从而建立 STDIO 通信通道。
Resources 是 MCP 的第二大原语,代表服务器暴露给应用程序读取的数据。与 Tools 不同,Resources 由应用(客户端)决定何时读取,而非由模型决定。
Tools 是"动作"——模型主动调用来完成任务;Resources 是"数据"——应用按需读取,类似只读的数据源。典型的资源包括:配置文件、数据库记录、系统状态信息等。
使用 `@mcp.resource()` 装饰器,传入资源的 URI。URI 是资源的唯一标识,客户端通过 URI 来请求读取资源内容。
from mcp.server.fastmcp import FastMCP
mcp = FastMCP('my-server')
# 静态资源:固定 URI
@mcp.resource('resource://config/app')
def get_app_config() -> str:
'''返回应用配置文件内容'''
with open('config.json', 'r', encoding='utf-8') as f:
return f.read()
# 动态资源:URI 模板(使用 {参数} 占位符)
@mcp.resource('resource://files/{filename}')
def get_file(filename: str) -> str:
'''读取 data 目录下指定文件的内容'''
safe_path = f'./data/{filename}'
with open(safe_path, 'r', encoding='utf-8') as f:
return f.read()
if __name__ == '__main__':
mcp.run()资源 URI 支持模板语法(如 `resource://files/{filename}`),大括号中的部分会映射到函数参数。这让一个资源定义可以处理多个具体路径。
Resource 适合暴露只读数据,如配置文件、数据库查询结果等。写操作和有副作用的动作应使用 Tool。
定义好资源之后,客户端如何读取它们?本节你将在客户端代码中调用 `list_resources()` 和 `read_resource()`,掌握完整的资源访问流程。
`session.list_resources()` 返回服务器上所有已注册资源的元数据,包括 URI、名称和描述。注意 URI 模板资源会以模板形式列出。
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
server_params = StdioServerParameters(command='python', args=['server.py'])
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
# 列出所有资源
resources = await session.list_resources()
print('可用资源:')
for r in resources.resources:
print(f' URI: {r.uri}')
print(f' 描述: {r.description}')
# 读取具体资源
content = await session.read_resource('resource://config/app')
print(f'\n配置内容:')
for item in content.contents:
print(item.text)
if __name__ == '__main__':
asyncio.run(main())`session.read_resource(uri)` 接受一个具体的 URI 字符串(不是模板),返回资源内容。对于 URI 模板资源,需要传入填充后的完整 URI。
read_resource() 需要完整的、已填充参数的 URI。模板只是服务器声明支持哪些 URI 格式,实际读取时必须提供具体值。
Prompts 是 MCP 的第三大原语,由用户触发。它们是可复用的提示模板,允许用户用标准化的方式启动特定类型的对话。
Prompts 不是发给模型的消息,而是"消息模板"——带有占位参数的预设对话结构。用户或应用选择一个 Prompt、填入参数,得到可直接使用的消息列表。
使用 `@mcp.prompt()` 装饰器定义 Prompt 函数。函数接收模板参数,返回一个或多个 PromptMessage 对象。
from mcp.server.fastmcp import FastMCP
from mcp.types import PromptMessage, TextContent
mcp = FastMCP('my-server')
@mcp.prompt()
def code_review(code: str, language: str = 'python') -> list[PromptMessage]:
'''生成代码审查提示模板'''
return [
PromptMessage(
role='user',
content=TextContent(
type='text',
text=f'请审查以下 {language} 代码,指出潜在问题并给出改进建议:\n\n```{language}\n{code}\n```'
)
)
]
@mcp.prompt()
def summarize_document(document: str, style: str = '简洁') -> list[PromptMessage]:
'''生成文档摘要提示模板,支持指定摘要风格'''
return [
PromptMessage(
role='user',
content=TextContent(
type='text',
text=f'请以{style}的风格为以下文档写一段摘要:\n\n{document}'
)
)
]
if __name__ == '__main__':
mcp.run()每个 PromptMessage 包含 role('user' 或 'assistant')和 content(通常是 TextContent)。你可以返回多条消息来构建多轮对话模板。
Prompts 是 user-triggered 的,由用户或应用选择并触发。这与 Tools(模型决定调用)和 Resources(应用决定读取)不同。
最后一步:在客户端中列出并使用 Prompts。本节你将看到完整的 Prompt 使用流程,以及如何将 Prompt 结果整合到实际对话中。
`session.list_prompts()` 返回服务器上所有已注册 Prompt 的元数据,包括名称、描述和参数列表。这让客户端可以动态展示可用模板给用户。
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
server_params = StdioServerParameters(command='python', args=['server.py'])
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
# 列出所有 Prompts
prompts = await session.list_prompts()
print('可用 Prompts:')
for p in prompts.prompts:
print(f' 名称: {p.name}')
print(f' 描述: {p.description}')
print(f' 参数: {[arg.name for arg in (p.arguments or [])]}')
# 使用 code_review prompt
result = await session.get_prompt(
'code_review',
arguments={
'code': 'def add(a, b):\n return a + b',
'language': 'python'
}
)
print('\n生成的消息模板:')
for msg in result.messages:
print(f'[{msg.role}] {msg.content.text[:100]}...')
if __name__ == '__main__':
asyncio.run(main())`session.get_prompt(name, arguments)` 返回填充好参数的消息列表。这些消息可以直接传给 LLM API(如 Anthropic Claude)作为对话历史使用。
get_prompt() 返回已填充参数的 PromptMessage 列表,这些消息遵循标准的 role/content 格式,可以直接传给 Anthropic Claude 等 LLM API。
恭喜你完成了所有课程内容!现在用这套综合测评检验一下你对 MCP 的理解。每道题都考察一个核心知识点,认真作答吧。
MCP 是一个开放协议,标准化了 AI 模型(通过客户端)与外部工具和数据源(通过服务器)之间的通信方式,类似 AI 领域的 USB-C 接口。
Tools 是 model-controlled(模型决定调用),Resources 是 application-controlled(应用决定读取),Prompts 是 user-triggered(用户触发)。
docstring 会作为工具描述(description)传递给语言模型。模型依赖这段文字来理解工具的功能,从而判断在当前对话中是否应该调用它。
Resource 专为只读数据设计,由应用按需读取。有副作用的操作(执行命令、写数据库、发 HTTP 请求)应该使用 Tool。
`mcp dev` 是开发模式启动命令,会同时启动 MCP 服务器和 Inspector Web UI,方便开发者在不编写客户端的情况下测试工具。
`session.call_tool(name, arguments)` 是 ClientSession 提供的标准方法,用于调用服务器端已注册的工具并获取返回结果。
URI 模板中的 `{param}` 会映射到函数的同名参数。例如 `resource://files/{filename}` 中的 `{filename}` 对应函数的 `filename: str` 参数。
Prompt 函数应返回 list[PromptMessage],每个 PromptMessage 包含 role 和 content 字段,格式与 LLM API 的消息格式兼容。
你已经完成了整个 MCP 入门课程!让我们回顾一下这段学习旅程,并展望接下来可以构建的精彩内容。
从零开始,你已经掌握了构建完整 MCP 应用所需的全部基础知识:
掌握了基础之后,以下是一些可以马上动手的真实项目方向:
MCP 是 AI 工程领域的前沿技术。掌握它,你就拥有了将任何工具、数据库、API 与 AI 模型无缝连接的能力。这个技能将在未来的 AI 应用开发中变得越来越重要。继续探索,继续构建!
MCP 进阶专题涵盖 Sampling(服务器请求 LLM 调用)、Roots(文件访问安全机制)、StreamableHTTP(远程部署传输)等生产级特性,是自然的下一步。
想要测验互动、进度自动保存的完整体验?
进入 AI 学院互动版 →