本系列是笔者在学习MCP中的一些总结和笔记,所以主要以代码实践为主(PS:这也是为什么本系列的第一篇是uv的使用和安装),默认各位读者已经对MCP的概念有一些简单了解。当然一些读者可能只是简单知道MCP这个名词,并不清楚具体是什么东西。所以在这里附上我认为介绍MCP还不错的文章。MCP(模型上下文协议) MCP: 客户端与服务端交互流程 笔者这篇文章的代码来源也主要来自于这篇 MCP:编程实战
MCP简要介绍
MCP(Model Context Protocol)是由Anthropic提出的开源协议,旨在简化大型语言模型(LLM)与外部数据源、工具或资源的集成。它通过标准化规则,为LLM提供一种统一的接口,使其能够灵活连接和操作各种外部系统(如文件、数据库、API等),从而增强模型的实用性和扩展性。
MCP 采用客户端 - 服务器(Client-Server)架构,分为三个核心组件:
- MCP Host:运行 AI 应用的载体,如 Claude Desktop 或 IDE,负责发起请求。
- MCP Client:与服务器建立 1:1 连接的协议客户端,负责数据传输。
- MCP Server:轻量化程序,封装具体工具或数据源(如 GitHub、Slack),通过标准化接口暴露功能。
MCP与Function Call
Function Call(函数调用) 是大模型的基础能力,允许模型在推理过程中生成结构化指令调用外部工具(如 OpenAI 的 function calling 机制,想了解的请看这篇大模型工具调用(function call)原理及实现)。而 MCP 协议则是在此基础上构建的跨模型 / 工具通信框架,二者关系可概括为(大模型生成):
MCP与Agent
Agent(智能体) 是基于大模型构建的自主执行者,具备任务规划、工具调用和结果整合能力。MCP 协议与 Agent 的关系可理解为:
- MCP 是基础设施:为 Agent 提供统一的工具接入方式,避免重复开发适配代码。
- Agent 是上层应用:利用 MCP 协议动态调用工具,实现复杂流程自动化。
二者关系可概括为(大模型生成):
以下是大模型对MCP、Function Calling和Agent三者关系的解释,笔者认为还是非常可能的。 MCP与Function Calling正逐渐成为AI智能体(Agent)的核心基石,未来大模型的应用开发可能呈现AI Agent = LLM + Function Call +MCP的趋势。LLM提供智能决策和自然语言理解能力。Function Call实现精准的工具调用,解决单个任务的需求。MCP通过标准化协议,支持多工具协作,降低集成复杂度。 例如:在数据分析场景中,数据分析Agent 通过 MCP 协议连接 Excel 服务器读取数据,使用 Function Call 调用统计工具计算增长率,最后通过 MCP 协议将结果写入报告文档。
MCP服务类型
在 MCP中,服务端(MCP Server)可以为客户端(MCP Client)提供三类核心服务:Resources(资源)、Tools(工具) 和 Prompts(提示模板)。这三类服务通过标准化接口,为大语言模型(LLM)与外部系统的集成提供灵活性和扩展性。
Recources提供静态的、只读的上下文数据或内容,作为 LLM 的输入或参考。类似 REST API 的 GET 请求,通过 URI 风格的路径访问且不涉及修改外部系统的状态。。可以支持文本或二进制数据。适合缓存,减少重复请求对系统性能的影响。
典型的应用场景有 读取文件内容:例如从本地文件系统或云存储中读取文档、代码片段等。获取配置信息:读取系统或应用的配置参数(如数据库连接字符串、API 密钥等)。 查询数据库数据:从数据库中提取特定记录或统计信息。 获取系统状态:例如读取服务器的运行状态、日志信息等。
Tools允许 LLM 调用外部系统执行动态操作或计算。类似 REST API 的 POST/PUT/DELETE 请求,通过工具名称和参数调用,可以修改外部系统的状态或与外部系统交互。工具的范围从简单的计算到复杂的 API 调用(如发送邮件、执行 SQL 语句等)。客户端可以通过 tools/list 接口列出所有可用工具。
典型使用场景有 数学计算:执行加减乘除、统计分析等计算任务。数据处理:对数据进行清洗、转换或格式化。 API 调用:调用外部服务的 API(如发送邮件、查询天气、调用支付接口等)。 文件操作:创建、修改或删除文件(如保存草稿、上传文件等)。
Prompts定义可复用的提示模板(Prompt Templates),指导 LLM 的输出行为。 支持参数化模板,允许动态填充变量(如用户输入、上下文信息等)。MCP服务通过统一的模板格式,规范 LLM 的输出逻辑。模板可以被多个客户端共享,减少重复定义。 典型使用场景有生成文本模板:例如生成报告、合同、邮件草稿等结构化文本。定义对话流程:通过模板引导用户与 LLM 的交互(如客服对话、问答流程)。标准化输出:确保 LLM 的输出符合特定格式(如 JSON、Markdown 等)。任务指令:为复杂任务定义分步骤的提示模板(如代码生成、数据分析等)。
三类服务对比(大模型生成):
MCP服务端编程实现
MCP相关的概念讲完了,下面让我进入激动人心的编码环节。在此之前还要做一项烦人的准备工作——项目环境搭建。
环境搭建
- UV安装 :本文使用uv作为包管理工具(当然,读者也可以使用其他的包管理工具如conda)。uv的安装教程请看本系列的第一篇。
- Node.js安装:uv安装完成后,然后安装node.js。安装教程可参考win系统安装nodejs和linux系统安装node.js。然后对npm换源。如果读者只是想学习MCP开发,或者是一个刚入门的小白,那么之间安装node.js就行了。如果读者想使用MCP开发一个应用程序,或者有相关经验,那么笔者推荐先安装nvm,再使用nvm安装node.js。由于笔者使用的是Ubuntu系统,安装和换源分别通过Ubuntu安装nvm和nvm换源完成。
- PostgreSql安装(可选):如果uv和nodejs都已经搞定了,那么恭喜,你已经完成了项目创建的60%的工作。下面让我们安装pgsql数据库,安装教程:win系统安装PgSql和linux安装PgSql。这些教程需要的配置的很多,笔者可完成为postgres用户设置密码就可以了。创建用户和数据库会在下面讲到。 这是一个可选项,读者也可以不选择安装,只是在编写Resource服务时,会出现报错,但是对编写Prompt和Tools服务没有影响。
初始化项目
让我们使用uv初始化项目,命令
# 创建 python 项目,项目名称为 mcp_learning
uv init mcp_learning
cd mcp_learning
# 激活虚拟环境
uv venv
.venv\Scripts\activate
在这一步,如果读者没有将uv源换成国内源的话可能会出现连接超时的错误。
然后安装相关依赖:
# 安装相关依赖包
uv add mcp[cli] httpx
# 连接 pg 数据库的 python 包,如何读者没有安装数据库,那么也可以选择不安装
uv add psycopg2
创建服务端实例
# 导入必要的模块
import psycopg2 # PostgreSQL 数据库连接库
import json # JSON 处理库
from psycopg2.extras import RealDictCursor # 使查询结果以字典形式返回的游标
from mcp.server.fastmcp.prompts import base # MCP 提示模板的基础类
from mcp.server.fastmcp import FastMCP # 导入 MCP 服务器的主类
# 创建 MCP 服务器
mcp = FastMCP("MCP Test Server", # 服务器名称
debug=True, # 启用调试模式,会输出详细日志
host="0.0.0.0", # 监听所有网络接口,允许远程连接
port=8002) # 服务器监听的端口号
这段代码的作用是创建服务端名称为 "MCP Test Server" 的实例 启用了调试模式,产生更详细的日志输出,方便调试。 服务器配置为监听 0.0.0.0(所有网络接口),接受来自本地和远程的连接 端口设置为 8002,这是客户端连接服务器的端口 。 FastMCP 为 MCP 服务端的主要实现类。 注意:host="0.0.0.0" 设置使服务器可以接受来自任何 IP 地址的连接,这在开发环境中很方便,但在生产环境中可能需要更严格的访问控制。
Resources服务实现
如果读者没有安装PgSql数据库,那么可以跳过这一章节。如果已经安装了PgSql数据库,请跟着我左手右手一个慢动作。 首先我们要在PgSql中创建一个新的用户和数据库,第一步就是先登录PgSql。
psql -h localhost -U postgres -d postgres
# 然后输入密码进入pgsql命令行
创建一个新的用户和数据库
## 添加一个用户mcp_learning,并设置密码
create user mcp_learning with password '新的密码';
## 给 mcp_learning 用户,创建一个数据库叫mcp_learningdb
create database mcp_learningdb owner mcp_learning;
授予 mcp_learing 用户对于数据库 mcp_learingdb 的所有权限,以下4步是一个整体,不要跳着执行,否则会出现"明明给A授权的,却发现是给B授予"。参考PostgreSQL创建管理员与数据库。
-- 1) 先退出postgre数据库
\q
-- 2) 登录 mcp_learingdb 数据库
psql -h localhost -U postgres -d mcp_learingdb
-- 3) 将 mcp_learingdb 的所有权限赋予 mcp_learing
grant all privileges on database mcp_learingdb to mcp_learing;
-- 4) 授予 mcp_learing 对 mcp_learingdb 数据库的读写权限
grant usage on schema public to mcp_learing;
grant all privileges on all tables in schema public to mcp_learing;
grant all privileges on all sequences in schema public to mcp_learing;
grant select,insert,update,delete on all tables in schema public to mcp_learing;
grant all on schema public to mcp_learing;
在 mcp_learingdb 中创建数据表。
-- 1)登录 mcp_learingdb数据库
psql -h localhost -U mcp_learing -d mcp_learingdb
-- 2)创建表 company表
CREATE TABLE COMPANY(
ID INT PRIMARY KEY NOT NULL,
NAME TEXT NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR(50),
SALARY REAL
);
-- 3) 插入数据
INSERT INTO COMPANY
(ID,NAME,AGE,ADDRESS,SALARY) VALUES
(1, 'Mark', 25, 'Rich-Mond ', 65000.0 ),
(2, 'David', 27, 'Texas', 85000.00);
MCP 服务端可以为 MCP 客户端定义访问数据库数据的资源端点,允许客户端查询数据库的元数据、数据表数据。首先编写数据库连接。
# 数据库连接配置
DB_CONFIG = {
"dbname": "mcp_learningdb",
"user": "mcp_learning",
"password": "自己的密码",
"host": "localhost",
"port": "5432"
}
def get_db_connection():
"""创建数据库连接"""
return psycopg2.connect(**DB_CONFIG)
然后编写资源示例:
1.查询表名
查询数据库中public下的所有表名,并返回表名列表的JSON字符串。
# 定义资源:获取所有表名
@mcp.resource("db://tables")
def list_tables() -> str:
"""获取所有表名列表"""
with get_db_connection() as conn:
with conn.cursor() as cur:
cur.execute("""
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
""")
tables = [row[0] for row in cur.fetchall()]
return json.dumps(tables)
2.表数据查询
定义数据表查询的资源,允许查询指定表的数据,支持参数: table_name: 要查询的表名 limit: 限制返回的最大行数,默认值为 100 使用了 RealDictCursor 使结果以字典形式返回,使用了参数化查询来防止 SQL 注入攻击,并设置 ensure_ascii=False 以保留中文字符。
# 定义资源:获取表数据
@mcp.resource("db://tables/{table_name}/data/{limit}")
def get_table_data(table_name: str, limit: int = 100) -> str:
"""获取指定表的数据
参数:
table_name: 表名
"""
try:
with get_db_connection() as conn:
with conn.cursor(cursor_factory=RealDictCursor) as cur:
# 使用参数化查询防止 SQL 注入
cur.execute(f"SELECT * FROM %s LIMIT %s",
(psycopg2.extensions.AsIs(table_name), limit))
rows = cur.fetchall()
# return json.dumps(list(rows), default=str)
return json.dumps(list(rows), default=str, ensure_ascii=False)
except Exception as e:
return json.dumps({
"status": "error",
"message": str(e)
})
3.表结构查询
定义表结构查询的资源,允许查询指定表的结构信息,包括列名、数据类型、最大长度和列注释。(不同类型的数据库,查询表元数据的 sql 会有所不同)。
# 定义资源:获取表结构
@mcp.resource("db://tables/{table_name}/schema")
def get_table_schema(table_name: str) -> str:
"""获取表结构信息
参数:
table_name: 表名
"""
with get_db_connection() as conn:
with conn.cursor() as cur:
cur.execute("""
select c.column_name,
c.data_type,
c.character_maximum_length,
pgd.description as column_comment
from information_schema.columns c
left join pg_catalog.pg_statio_all_tables st
on c.table_schema = st.schemaname and c.table_name = st.relname
left join pg_catalog.pg_description pgd
on pgd.objoid = st.relid
and pgd.objsubid = c.ordinal_position
where c.table_name = %s
order by c.ordinal_position
""", (table_name,))
columns = [{"name": row[0], "type": row[1], "max_length": row[2], "comment": row[3]}
for row in cur.fetchall()]
return json.dumps(columns, ensure_ascii=False)
Prompts服务实现
定义 MCP prompt(提示模板),用于指导 LLM 如何回答特定类型的查询。
1.中国省份介绍
使用 @mcp.prompt() 装饰器注册为 MCP 服务器的提示模板,接受一个参数 province,表示要介绍的中国省份名称,返回一个字符串提示模板,引导 LLM 按照特定结构介绍指定省份 提示模板要求 LLM 从四个方面介绍省份:历史沿革、人文地理和风俗习惯、经济发展状况、旅游建议。
# 中国省份介绍
@mcp.prompt()
def introduce_china_province(province: str) -> str:
"""介绍中国某个省份"""
return f"""请介绍这个省份:{province}
要求介绍以下内容:
1. 历史沿革
2. 人文地理、风俗习惯
3. 经济发展状况
4. 旅游建议
"""
2.代码调试提示模板
提示模板的功能:
- 使用 @mcp.prompt() 装饰器注册
- 接受两个参数:code(需要调试的代码)和 error_message(错误信息)
- 与第一个模板不同,这个模板返回的是 list[base.Message] 类型,表示一个多轮对话的消息列表 对话包含五条消息:
- 系统消息:定义助手的角色和任务
- 用户消息:请求帮助修复代码
- 用户消息:包含代码内容(使用代码块格式)
- 用户消息:包含错误信息
- 助手消息:初始回应,表明将分析问题 使用场景:当用户遇到代码错误需要帮助调试时,通过预设对话历史,引导 LLM 进入特定的思考模式,提供结构化的上下文,使 LLM 能够更有效地分析和解决代码问题。
# 调试代码提示
@mcp.prompt()
def debug_code(code: str, error_message: str) -> list[base.Message]:
"""调试代码的对话式提示模板
参数:
code: 需要调试的代码
error_message: 错误信息
"""
return [
base.AssistantMessage("你是一位专业的代码调试助手。请仔细分析用户提供的代码和错误信息,找出问题所在并提供修复方案。"),
base.UserMessage("我的代码有问题,请帮我修复:"),
base.UserMessage(f"```\n{code}\n```"),
base.UserMessage(f"错误信息:\n{error_message}"),
base.AssistantMessage("我会帮你分析这段代码和错误信息。首先让我理解问题所在..."),
]
Tools 实现
Tools实现是最简单的,下面的代码定义了四个基本的数学运算工具,通过 @mcp.tool() 装饰器,将这些函数注册为 MCP 服务端的工具(可以被客户端直接调用)
@mcp.tool()
def add(a: float, b: float) -> float:
"""加法运算
参数:
a: 第一个数字
b: 第二个数字
返回:
两数之和
"""
return a + b
@mcp.tool()
def subtract(a: float, b: float) -> float:
"""减法运算
参数:
a: 第一个数字
b: 第二个数字
返回:
两数之差 (a - b)
"""
return a - b
@mcp.tool()
def multiply(a: float, b: float) -> float:
"""乘法运算
参数:
a: 第一个数字
b: 第二个数字
返回:
两数之积
"""
return a * b
@mcp.tool()
def divide(a: float, b: float) -> float:
"""除法运算
参数:
a: 被除数
b: 除数
返回:
两数之商 (a / b)
异常:
ValueError: 当除数为零时
"""
if b == 0:
raise ValueError("除数不能为零")
return a / b
MCP Inspector验证服务功能
MCP Inspector 是专为 Model Context Protocol(MCP)服务端设计的交互式调试工具,提供了一个直观的界面,使得开发者能够快速地验证服务端的响应和状态。使用 MCP Inspector 来测试验证上述开发的服务端功能。
运行命令:mvp dev server_see_test.py( mcp dev 命令默认使用 stdio 通信方式来启动 mcp 服务端),对上面开发的 mcp 服务端功能进行测试。点击输出的链接,即可打开浏览器,进入 mcp inspector 用户界面:
剩下的功能验证内容就和这篇文章的内容一模一样了 MCP:编程实战,大家可以参考这篇文章对自己写的MCP服务进行验证测试。
我计划创建一个MCP学习群,大家可以在群里讨论问题,当然不限于MCP,也会不定期分享一些大模型学习资料。有兴趣可以添加jsmxok。添加时请备注 学习)