Day30-MCP
https://kq4b3vgg5b.feishu.cn/wiki/SVJOwHAjHiTXyskeRr7cL03invb
https://kq4b3vgg5b.feishu.cn/wiki/NmBIwIujUi27L4kNFNjc98wRnOf
MCP快速入门实战
- Anthropic MCP发布通告:https://www.anthropic.com/news/model-context-protocol
- MCP GitHub主页:https://github.com/modelcontextprotocol
一、MCP技术体系介绍
MCP,全称是Model Context Protocol,模型上下文协议,由Claude母公司Anthropic于2024年11月正式提出。
MCP刚发布的时候不温不火,直到今年Agent大爆发才被广泛关注。而在今年2月,Cursor正式宣布加入MCP功能支持,一举将MCP推到了全体开发人员面前。从本质上来说,MCP是一种技术协议,一种智能体Agent开发过程中共同约定的一种规范。这就好比秦始皇的“书同文、车同轨”,在统一的规范下,大家的协作效率就能大幅提高,最终提升智能体Agent的开发效率。截止目前,已上千种MCP工具诞生,在强悍的MCP生态加持下, 人人手搓Manus的时代即将到来。
总的来说,MCP解决的最大痛点,就是Agent开发中调用外部工具的技术门槛过高的问题。
我们都知道,能调用外部工具,是大模型进化为智能体Agent的关键,如果不能使用外部工具,大模型就只能是个简单的聊天机器人,甚至连查询天气都做不到。由于底层技术限制啊,大模型本身是无法和外部工具直接通信的,因此Function calling的思路,就是创建一个外部函数(function)作为中介,一边传递大模型的请求,另一边调用外部工具,最终让大模型能够间接的调用外部工具。
Function calling是个非常不错的技术设计,自诞生以来,一直被业内奉为圭臬。但唯一的问题就是,编写这个外部函数的工作量太大了,一个简单的外部函数往往就得上百行代码,而且,为了让大模型“认识”这些外部函数,我们还要额外为每个外部函数编写一个JSON Schema格式的功能说明,此外,我们还需要精心设计一个提示词模版,才能提高Function calling响应的准确率。
而MCP的目标,就是能在Agent开发过程中,让大模型更加便捷的调用外部工具。为此,MCP提出了两个方案,其一,“车同轨、书同文”,统一Function calling的运行规范。
首先是先统一名称,MCP把大模型运行环境称作 MCP Client,也就是MCP客户端,同时,把外部函数运行环境称作MCP Server,也就是MCP服务器。
然后,统一MCP客户端和服务器的运行规范,并且要求MCP客户端和服务器之间,也统一按照某个既定的提示词模板进行通信。
“车同轨、书同文”最大的好处就在于,可以避免MCP服务器的重复开发,也就是避免外部函数重复编写。例如,像查询天气、网页爬取、查询本地MySQL数据库这种通用的需求,大家有一个人开发了一个服务器就好,开发完大家都能复制到自己的项目里来使用,不用每个人每次都单独写一套。
这可是促进全球AI开发者共同协作的好事儿,很快,GitHub上就出现了海量的已经开发好的MCP 服务器,从SQL数据库检索、到网页浏览信息爬取,从命令行操作电脑、到数据分析机器学习建模,等等等等,不一而足。
现在,只要你本地运行的大模型支持MCP协议,也就是只要安装了相关的库,仅需几行代码即可接入这些海量的外部工具,是不是感觉Agent开发门槛瞬间降低了呢。
这种“车同轨、书同文”的规范,在技术领域就被称作协议,例如http就是网络信息交换的技术协议。各类技术协议的目标,都是希望通过提高协作效率来提升开发效率,而MCP,Model Context Protocol,就是一种旨在提高大模型Agent开发效率的技术协议。
那既然是协议,必然是使用的人越多才越有用。因此,为了进一普及MCP协议,Anthropic还提供了一整套MCP客户端、服务器开发的SDK,也就是开发工具,并且支持Python、TS和Java等多种语言,借助SDK,仅需几行代码,就可以快速开发一个MCP服务器。
然后,你就可以把它接入任意一个MCP客户端来构建智能体,如果愿意,还可以把MCP服务器分享到社区,给有需求的开发者使用,甚至你还可以把你的MCP服务器放到线上运行,让用户付费使用。
而MCP的客户端,不仅支持Claude模型,也支持任意本地模型或者在线大模型,或者是一些IDE。例如,现在Cursor正式接入MCP,代表着Cursor正式成为MCP客户端,在Cursor中,我们不仅能快速编写MCP服务器(外部函数),更能借助Cursor一键连接上成百上千的开源MCP服务器,让大模型快速接入海量工具,从而大幅加快Agent开发进度。
2025年注定是智能体爆发的一年啊而MCP技术则必然会大幅加快这个进程!
二、MCP客户端Client开发流程
1. uv工具入门使用指南
1.1 uv入门介绍
MCP开发要求借助uv进行虚拟环境创建和依赖管理。uv 是一个Python 依赖管理工具,类似于 pip 和 conda,但它更快、更高效,并且可以更好地管理 Python 虚拟环境和依赖项。它的核心目标是替代 pip,提供更好的性能和更低的管理开销。
uv 的特点:
- 速度更快:相比
pip,uv性能更优。 - 兼容
pip:支持requirements.txt依赖管理。 - 替代
venv:提供uv venv进行虚拟环境管理,比venv更轻量。 - 跨平台:支持 Windows、macOS 和 Linux。
1.2 uv安装流程
使用 pip 安装(适用于已安装 pip 的系统)
pip install uv
1.3 uv的基本用法介绍
安装 uv 后,你可以像 pip 一样使用它,但它的语法更简洁,速度也更快。注意,以下为使用语法示例,不用实际运行。
-
创建虚拟环境
uv venv myenv -
激活虚拟环境
source myenv/bin/activate # Linux/macOS myenv\Scripts\activate # Windows -
安装 Python 依赖
uv pip install xxx -
运行python项目
uv run python xxx.py
为什么MCP更推荐使用uv进行环境管理?
MCP 依赖的 Python 环境可能包含多个模块,
uv可以提供更高效的管理方式,并且可以避免pip的一些依赖冲突问题。此外,uv的包管理速度远超pip,这对于 MCP 这样频繁管理依赖的项目来说是一个很大的优势。
2.MCP极简客户端搭建流程
接下来我们尝试先构建一个 MCP 客户端,确保基本逻辑可用,然后再逐步搭建 MCP 服务器进行联调,这样可以分阶段排查问题,避免一上来就涉及太多复杂性。
2.1 创建 MCP 客户端项目
# 创建项目目录
uv init mcp-client
cd mcp-client

| 文件/文件夹 | 作用 |
|---|---|
.git/ |
Git 版本控制目录 |
.venv/ |
虚拟环境 |
.gitignore |
Git 忽略规则 |
.python-version |
Python版本声明 |
main.py |
主程序入口 |
pyproject.toml |
项目配置文件 |
README.md |
项目说明文档 |
2.2 创建MCP客户端虚拟环境
# 创建虚拟环境
uv venv
# 激活虚拟环境
source .venv/bin/activate #mac/linux
.venv\Scripts\activate #windows
这里需要注意的是,相比pip,uv会自动识别当前项目主目录并创建虚拟环境。然后即可通过add方法在虚拟环境中安装相关的库。首先安装MCP SDK:
MCP SDK 是一个开发工具包,帮助开发者快速构建符合 MCP 标准的客户端或服务器,实现 AI 模型与外部工具的安全高效连接,支持动态适配工具参数变化并减少重复开发。
# 安装 MCP SDK
uv add mcp
编写项目源码
接下来在主目录下创建/src/pro_name作为代码主目录,在其内部创建server和client脚本文件。
2.3 编写基础 MCP 客户端
然后在当前项目主目录中**创建 client.py **
type nul > client.py #windows
touch client.py #mac/linux
然后写入如下通用client的代码:
#Python 内置的异步编程库,让 MCP 可以非阻塞地执行任务(比如聊天、查询)。
import asyncio
#用于管理 MCP 客户端会话(但目前我们先不连接 MCP 服务器)。
from mcp import ClientSession
#自动管理资源,确保程序退出时正确关闭 MCP 连接。
from contextlib import AsyncExitStack
#定义 MCPClient 类
class MCPClient:
def __init__(self):
"""初始化 MCP 客户端"""
self.session = None #存储与 MCP 服务器的会话对象.暂时不连接 MCP 服务器,后续可以修改来真正连接。
#创建资源管理器,管理 MCP 客户端的资源,确保程序退出时可以正确释放资源。
self.exit_stack = AsyncExitStack()
#模拟连接 MCP 服务器,这是一个异步方法,目前只是打印一条消息,表示客户端已初始化但未真正连接到服务器。在实际应用中,这里会调用 ClientSession 来建立与真实 MCP 服务器的连接。
async def connect_to_mock_server(self):
"""模拟 MCP 服务器的连接(暂不连接真实服务器)"""
print("✅ MCP 客户端已初始化,但未连接到服务器")
#多轮对话:这是客户端的核心功能,允许用户通过命令行与 AI 模型交互。
async def chat_loop(self):
"""运行交互式聊天循环"""
print("\nMCP 客户端已启动!输入 'quit' 退出")
while True:
try:
query = input("\nQuery: ").strip()
if query.lower() == 'quit':
break
print(f"\n🤖 [Mock Response] 你说的是:{query}")
except Exception as e:
print(f"\n⚠️ 发生错误: {str(e)}")
#清理资源:关闭所有通过 AsyncExitStack 管理的异步资源(比如会话、文件句柄等),确保程序退出时不会留下未释放的资源和正确关闭 MCP 连接(尽管目前没有真正的连接)。
async def cleanup(self):
"""清理资源"""
await self.exit_stack.aclose()
#主函数:创建 MCPClient 实例。
async def main():
#创建一个 MCP 客户端实例。
client = MCPClient()
try:
#初始化 MCP 客户端(暂不连接服务器)。
await client.connect_to_mock_server()
#启动交互式聊天。
await client.chat_loop()
finally:
#确保 不管程序是否异常退出,都会正确释放资源。
await client.cleanup()
if __name__ == "__main__":
#运行整体程序
asyncio.run(main())
MCP中一个基础的客户端代码结构总结如下:
| 代码部分 | 作用 |
|---|---|
MCPClient.__init__() |
初始化 MCP 客户端 |
connect_to_mock_server() |
模拟 MCP 服务器连接 |
chat_loop() |
提供交互式聊天界面 |
cleanup() |
释放资源 |
main() |
启动客户端 |
asyncio.run(main()) |
运行程序 |
2.5 运行 MCP 客户端
然后尝试运行这个极简的MCP客户端:
uv run client.py
3. MCP客户端接入DeepSeek在线模型
接下来尝试在客户端中接入OpenAI和DeepSeek等在线模型进行对话。需要注意的是,由于OpenAI和DeepSeek调用方法几乎完全一样,因此这套服务器client代码可以同时适用于GPT模型和DeepSeek模型。
3.1 新增依赖
为了支持调用在线模型,以及在环境变量中读取API-KEY等信息,需要先安装如下依赖:
uv add mcp openai python-dotenv
3.2 创建.env文件
创建.env文件,并写入大模型的API-Key,以及base url地址和模型名称等。
type nul > .env #windows touch .env #mac/linux
BASE_URL=https://api.deepseek.com
MODEL=deepseek-chat
OPENAI_API_KEY="DeepSeek API-Key"
3.3 修改client.py代码
接下来修改客户端代码:
import asyncio
import os
from openai import OpenAI
from dotenv import load_dotenv
from contextlib import AsyncExitStack
# 自动加载 .env 文件,避免在代码中直接暴露 API Key。
load_dotenv()
class MCPClient:
def __init__(self):
"""初始化 MCP 客户端"""
self.exit_stack = AsyncExitStack()
# 读取 OpenAI API Key
self.openai_api_key = os.getenv("OPENAI_API_KEY")
# 读取 BASE YRL
self.base_url = os.getenv("BASE_URL")
# 读取 model
self.model = os.getenv("MODEL")
if not self.openai_api_key:
raise ValueError("❌ 未找到 OpenAI API Key,请在 .env 文件中设置 OPENAI_API_KEY")
self.client = OpenAI(api_key=self.openai_api_key, base_url=self.base_url)
#发送用户输入到大模型
async def process_query(self, query: str) -> str:
"""调用 OpenAI API 处理用户查询"""
messages = [{"role": "system", "content": "你是一个智能助手,帮助用户回答问题。"},
{"role": "user", "content": query}]
try:
# 调用 OpenAI API
response = await asyncio.get_event_loop().run_in_executor(
None,
lambda: self.client.chat.completions.create(
model=self.model,
messages=messages
)
)
return response.choices[0].message.content
except Exception as e:
return f"⚠️ 调用 OpenAI API 时出错: {str(e)}"
async def chat_loop(self):
"""运行交互式聊天循环"""
print("\n🤖 MCP 客户端已启动!输入 'quit' 退出")
while True:
try:
query = input("\n你: ").strip()
if query.lower() == 'quit':
break
response = await self.process_query(query) # 发送用户输入到 OpenAI API
print(f"\n🤖 OpenAI: {response}")
except Exception as e:
print(f"\n⚠️ 发生错误: {str(e)}")
async def cleanup(self):
"""清理资源"""
await self.exit_stack.aclose()
async def main():
client = MCPClient()
try:
await client.chat_loop()
finally:
await client.cleanup()
if __name__ == "__main__":
asyncio.run(main())
3.4 运行client.py
然后即可输入如下命令开始运行对话客户端:
uv run client.py
4. MCP客户端接入本地ollama模型流程
接下来,我们继续尝试将ollama模型调度框架接入MCP的client。由于ollama和vLLM均支持OpenAI API风格调用方法,因此上述client.py并不需要进行任何修改,我们只需要启动响应的调度框架服务,然后修改.env文件即可。
4.1 MCP客户端接入本地ollama
这里以deepseek-r1:7b为例,尝试借助ollama接入MCP客户端。
-
启动ollama
ollama start -
修改
.env配置文件BASE_URL=http://localhost:11434/v1/ MODEL=deepseek-r1:7b OPENAI_API_KEY=ollama -
运行MCP client客户端了
uv run client.py
三、MCP天气查询服务器server与使用
1. MCP服务器通讯机制
Model Context Protocol(MCP)是一种由 Anthropic 开源的协议,根据 MCP 的规范,当前支持两种传输方式:标准输入输出(stdio)和基于 HTTP 的服务器推送事件(SSE)。
在 Model Context Protocol(MCP)中,标准输入输出(stdio)模式是一种用于本地通信的传输方式。在这种模式下,MCP 客户端会将服务器程序作为子进程启动,双方通过标准输入(stdin)和标准输出(stdout)进行数据交换。这种方式适用于客户端和服务器在同一台机器上运行的场景,确保了高效、低延迟的通信。
具体而言,客户端通过标准输入发送请求,服务器通过标准输出返回响应。这种直接的数据传输方式减少了网络延迟和传输开销,适合需要快速响应的本地应用。
相比之下,MCP 还支持基于 HTTP 和服务器推送事件(SSE)的传输方式,适用于客户端和服务器位于不同物理位置的场景。在这种模式下,客户端和服务器通过 HTTP 协议进行通信,利用 SSE 实现服务器向客户端的实时数据推送。
总的来说,stdio 模式提供了一种简单、高效的本地通信方式,适用于客户端和服务器在同一环境下运行的情况。而对于分布式或远程部署的场景,基于 HTTP 和 SSE 的传输方式则更为合适。
接下来我们尝试一个入门级的示例,那就是创建一个天气查询的服务器。通过使用OpenWeather API,创建一个能够实时查询天气的服务器(server),并使用stdio方式进行通信。
2. 天气查询服务器Server创建流程
2.1 服务器依赖安装
由于我们需要使用http请求来查询天气,因此需要在当前虚拟环境中添加如下依赖
uv add mcp httpx
2.2 服务器代码编写
接下来尝试创建服务器代码,此时MCP基本执行流程如下:
对应server服务器代码如下:
import json
import httpx
from typing import Any
from mcp.server.fastmcp import FastMCP
# 初始化 MCP 服务器,服务器名称可以自定义
mcp = FastMCP("WeatherServer")
# OpenWeather API 配置
OPENWEATHER_API_BASE = "https://api.openweathermap.org/data/2.5/weather"
API_KEY = "YOUR_API_KEY" # 请替换为你自己的 OpenWeather API Key
#请求载体身份标识,可以自定义
USER_AGENT = "weather-app/1.0"
#异步获取天气数据
async def fetch_weather(city: str) -> dict[str, Any] | None:
"""
从 OpenWeather API 获取天气信息。
:param city: 城市名称(需使用英文,如 Beijing)
:return: 天气数据字典;若出错返回包含 error 信息的字典
"""
#定义请求参数
params = {
"q": city,
"appid": API_KEY,
"units": "metric",
"lang": "zh_cn"
}
#定义请求头
headers = {"User-Agent": USER_AGENT}
#发送异步 GET 请求到 OpenWeather API。
async with httpx.AsyncClient() as client:
try:
response = await client.get(OPENWEATHER_API_BASE, params=params, headers=headers, timeout=30.0)
response.raise_for_status()
return response.json() # 返回字典类型
except httpx.HTTPStatusError as e:
return {"error": f"HTTP 错误: {e.response.status_code}"}
except Exception as e:
return {"error": f"请求失败: {str(e)}"}
#格式化天气数据
def format_weather(data: dict[str, Any] | str) -> str:
"""
将天气数据格式化为易读文本。
:param data: 天气数据(可以是字典或 JSON 字符串)
:return: 格式化后的天气信息字符串
"""
# 如果传入的是字符串,则先转换为字典
if isinstance(data, str):
try:
data = json.loads(data)
except Exception as e:
return f"无法解析天气数据: {e}"
# 如果数据中包含错误信息,直接返回错误提示
if "error" in data:
return f"⚠️ {data['error']}"
# 提取数据时做容错处理
city = data.get("name", "未知")
country = data.get("sys", {}).get("country", "未知")
temp = data.get("main", {}).get("temp", "N/A")
humidity = data.get("main", {}).get("humidity", "N/A")
wind_speed = data.get("wind", {}).get("speed", "N/A")
# weather 可能为空列表,因此用 [0] 前先提供默认字典
weather_list = data.get("weather", [{}])
description = weather_list[0].get("description", "未知")
return (
f"🌍 {city}, {country}\n"
f"🌡 温度: {temp}°C\n"
f"💧 湿度: {humidity}%\n"
f"🌬 风速: {wind_speed} m/s\n"
f"🌤 天气: {description}\n"
)
#定义MCP工具函数
@mcp.tool()
async def query_weather(city: str) -> str:
"""
输入指定城市的英文名称,返回今日天气查询结果。
:param city: 城市名称(需使用英文)
:return: 格式化后的天气信息
"""
data = await fetch_weather(city)
return format_weather(data)
if __name__ == "__main__":
# 以标准 I/O 方式运行 MCP 服务器
mcp.run(transport='stdio')
上述代码有两个注意事项,
- query_weather函数的函数说明至关重要,相当于是此后客户端对函数进行识别的基本依据,因此需要谨慎编写;
- 当指定
transport='stdio'运行 MCP 服务器时,客户端必须在启动时同时启动当前这个脚本,否则无法顺利通信。这是因为stdio模式是一种本地进程间通信方式,它需要服务器作为子进程运行,并通过标准输入输出(stdin/stdout)进行数据交换。
当我们编写完服务器后,并不能直接调用这个服务器,而是需要创建一个对应的能够进行stdio的客户端,才能顺利进行通信。
3. 天气查询客户端client创建流程
3.1 代码编写
import asyncio
import os
import json
from typing import Optional
from contextlib import AsyncExitStack
from openai import OpenAI
from dotenv import load_dotenv
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
# 加载 .env 文件,确保 API Key 受到保护
load_dotenv()
class MCPClient:
def __init__(self):
"""初始化 MCP 客户端"""
self.exit_stack = AsyncExitStack()
self.openai_api_key = os.getenv("OPENAI_API_KEY") # 读取 OpenAI API Key
self.base_url = os.getenv("BASE_URL") # 读取 BASE YRL
self.model = os.getenv("MODEL") # 读取 model
if not self.openai_api_key:
raise ValueError("❌ 未找到 OpenAI API Key,请在 .env 文件中设置 OPENAI_API_KEY")
self.client = OpenAI(api_key=self.openai_api_key, base_url=self.base_url) # 创建OpenAI client
#用于保存 MCP 的客户端会话,默认是 None,稍后通过 connect_to_server 进行连接。
self.session: Optional[ClientSession] = None
#连接指定server端,参数为server端文件路径
async def connect_to_server(self, server_script_path: str):
#判断服务器脚本是 Python 还是 Node.js,选择对应的运行命令。
is_python = server_script_path.endswith('.py')
is_js = server_script_path.endswith('.js')
if not (is_python or is_js):
raise ValueError("服务器脚本必须是 .py 或 .js 文件")
command = "python" if is_python else "node"
#告诉 MCP 客户端如何启动服务器。
server_params = StdioServerParameters(
command=command,
args=[server_script_path],
env=None
)
# 启动服务器进程,并建立 标准 I/O 通信管道
stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
#拿到读写流。
self.stdio, self.write = stdio_transport
#创建 MCP 客户端会话,与服务器交互。
self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))
#发送初始化消息给服务器,等待服务器就绪。
await self.session.initialize()
# 向 MCP 服务器请求所有已注册的工具(用 @mcp.tool() 标记)。
response = await self.session.list_tools()
tools = response.tools
print("\n已连接到服务器,支持以下工具:", [tool.name for tool in tools])
#使用大模型处理查询并调用可用的 MCP 工具 (Function Calling)
async def process_query(self, query: str) -> str:
#收到用户输入后,先把它组装进一个 messages 列表,目前只包含用户信息({"role": "user", "content": query})。
messages = [{"role": "user", "content": query}]
# 向 MCP 服务器请求所有已注册的工具(用 @mcp.tool() 标记)。
response = await self.session.list_tools()
#获取服务器上的工具,再转换成 available_tools 的格式。方便后面发给模型,告诉它:可以调用这些工具。
available_tools = [{
"type": "function",
"function": {
"name": tool.name,
"description": tool.description,
"input_schema": tool.inputSchema
}
} for tool in response.tools]
# print(available_tools)
#模型调用first response
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
tools=available_tools
)
# 处理返回的内容
content = response.choices[0]
if content.finish_reason == "tool_calls":
# 如何是需要使用工具,就解析工具
tool_call = content.message.tool_calls[0]
tool_name = tool_call.function.name
tool_args = json.loads(tool_call.function.arguments)
# 执行工具(手动调用外部工具函数)
result = await self.session.call_tool(tool_name, tool_args)
print(f"\n\n[Calling tool {tool_name} with args {tool_args}]\n\n")
# 将模型返回的调用哪个工具数据和工具执行完成后的数据都存入messages中
messages.append(content.message.model_dump())
messages.append({
"role": "tool",
"content": result.content[0].text,
"tool_call_id": tool_call.id,
})
# 将上面的结果再返回给大模型用于生产最终的结果
#second response
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
)
return response.choices[0].message.content
return content.message.content
#多轮对话
async def chat_loop(self):
"""运行交互式聊天循环"""
print("\n🤖 MCP 客户端已启动!输入 'quit' 退出")
while True:
try:
query = input("\n你: ").strip()
if query.lower() == 'quit':
break
response = await self.process_query(query) # 发送用户输入到 OpenAI API
print(f"\n🤖 OpenAI: {response}")
except Exception as e:
print(f"\n⚠️ 发生错误: {str(e)}")
#清理资源
async def cleanup(self):
"""清理资源"""
await self.exit_stack.aclose()
async def main():
client = MCPClient()
try:
await client.connect_to_server(sys.argv[1])
await client.chat_loop()
finally:
await client.cleanup()
if __name__ == "__main__":
import sys
asyncio.run(main())
3.2 测试运行
uv run client.py server.py
四、MCP服务器部署
对于MCP服务器来说,一般有两种部署方式,分别是在线发布和离线拷贝运行。
- MCP服务器在线发布
- 所谓在线发布,指的是我们可以将开发好的MCP服务器打包上传到pypi平台或者npm平台进行云平台托管,一旦上传成功,后续用户即可使用uv或者npx进行下载和使用。而各大MCP工具平台,如Smithery,本质上也是在pipy平台或者npm平台上托管的库基础上进行的进一步维护。
- pipy:https://pypi.org/
- npm:https://www.npmjs.com/
- MCP服务器离线拷贝运行
- 而所谓的离线部署运行,指的是将打包好的库直接拷贝到服务器上或者给他人进行使用,没有在云平台托管的这个环节,适合非公开MCP服务器进行部署。
1. 项目完善
尽管此前我们已经完成了几个示例项目的核心脚本编写,但其仍然不算是一个结构完整的项目。核心脚本的调用关系并不明确,同时项目说明也不够完善。因此这里我们首先需要先完善项目的主体内容,再考虑进行部署或上线发布。
1.1 src layout项目结构
其实在此之前,我们可以将代码都放在src内的某个文件夹里,这种项目结构也被称作src layout项目结构,这是一种非常通用、同时也便于代码维护的项目结构,基本示意图如下:
your-project/
├── src/ ← ✅ 所有业务代码都集中在这里
│ └── your_package/ ← Python 包的实际内容(含 __init__.py)
├── dist/ ← 构建后的分发包(如 wheel)
├── .venv/ ← 虚拟环境(本地依赖隔离)
├── .git/ ← Git 管理
├── pyproject.toml ← 项目配置
├── README.md ← 说明文件
├── .python-version ← Python版本声明
├── .gitignore ← Git 忽略规则
└── uv.lock ← uv 的依赖锁文件
接下来我们还需要在src/pro_name中创建两个py脚本,其一是__init__.py,使当前文件夹可以作为Python的一个库进行导入,需要在__init__.py写入如下代码:
from .server import main
同时再创建一个__main__.py,用于实际执行主函数调用流程:
from pro_name import main
main()
1.2 修改pyproject.toml
创建完基本项目结构后,让我们回到当前项目主目录下,删除main.py(如果有的话),然后修改项目配置文件pyproject.toml该配置文件原有内容如下:
注意:mcp-client为项目名称,需要大家自行替换成自己的项目名称!
[project]
name = "mcp-client"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"httpx>=0.28.1",
"mcp>=1.9.4",
"openai>=1.91.0",
"python-dotenv>=1.1.1",
]
该原有配置含义如下:
-
定义项目的元数据,包括:
-
项目名称、版本、描述
-
Python 最低版本要求
-
自动安装的依赖项(相当于
requirements.txt)
-
需要在原有内容头尾各自添加相关内容,完整配置如下:
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "mcp-client"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"httpx>=0.28.1",
"mcp>=1.9.4",
"openai>=1.91.0",
"python-dotenv>=1.1.1",
]
[project.scripts]
mcp-client = "mcp-client:main"
[tool.setuptools]
package-dir = {"" = "src"}
[tool.setuptools.packages.find]
where = ["src"]
具体配置解释如下:[build-system] 段
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
告诉 Python 构建工具:
- 要使用
setuptools来构建项目 - 同时依赖
wheel,因为你要构建.whl包
具体配置解释如下:[project.scripts]命令行入口
[project.scripts]
mcp-client = "mcp-client:main"
它会自动调用你包内的pro_name/__main__.py 里的 main() 函数
具体配置解释如下:[tool.setuptools]和[tool.setuptools.packages.find]
[tool.setuptools]
package-dir = {"" = "src"}
[tool.setuptools.packages.find]
where = ["src"]
明确告诉 setuptools:
- 你的项目源代码都在
src/目录下 - 请去
src/中查找 Python 包(即包含__init__.py的目录)
配置总结
| 区块 | 作用 |
|---|---|
[build-system] |
告诉构建工具如何构建项目 |
[project] |
定义项目基本信息和依赖 |
[project.scripts] |
定义命令行工具 |
[tool.setuptools] |
指定源码位置 |
1.3 然后对整个项目进行打包
注意是对server打包,因此src中不可以包含client.py
首先需要安装打包(和上传)工具:
uv pip install build twine
然后使用如下命令进行打包:
# 打包
cd 回到项目主目录下
.venv\Scripts\activate
python -m build
打包完成后会在主目录下新增一个dist文件夹:
以及在src目录下会创建一个.egg-info文件夹,
这两个文件夹内容解释如下:
-
dist/文件夹:打包产物目录,会包含如下文件:文件类型 说明 .whl文件Python wheel 格式的二进制分发包,最常用 .tar.gz文件源代码包(source distribution) 而在离线部署时候,别人安装时只要
pip install xxx.whl就可以了(无需源码),同时上传到 PyPI、内网 PyPI、AI 模型平台等地方部署。 -
<project-name>.egg-info/文件夹:元数据文件夹,这个是setuptools在构建或打包时生成的一个“项目元信息目录”。比如会看到这样的结构:mcp_server_deepresearch.egg-info/ ├── PKG-INFO ├── SOURCES.txt ├── top_level.txt ├── dependency_links.txt ├── requires.txt PKG-INFO:你项目的基本信息(名称、版本、作者、依赖等),requires.txt:列出依赖项,SOURCES.txt:列出所有打包进 .whl 的文件,top_level.txt:顶层包名(比如 mcp_server_deepresearch)。
2. 项目离线拷贝
接下来继续介绍离线拷贝流程。假设我们现在需要从服务器A上把项目拷贝到服务器B上,最简单的方法首先是在服务器A上进行项目压缩:
# 回到主目录
# cd /root/autodl-tmp/MCP/pro_name
tar --exclude='.venv' -czvf pro_name.tar.gz *
压缩后会创建一个名为pro_name.tar.gz的文件,将其下载后即可上传到服务器B上。然后即可使用如下命令进行解压缩:
tar -xzvf pro_name.tar.gz
然后即可激活虚拟环境并相关依赖:
# 创建虚拟环境
uv venv
# 激活虚拟环境
source .venv/bin/activate #mac linux
.venv\Scripts\activate #windows
# 使用uv(推荐)或pip安装
uv pip install .
最后即可运行测试了!
3. 项目发布到pipy平台
除了离线拷贝之外,我们也可以把自己开发完成的MCP Server发布到一些托管平台上方便其他人下载使用,如果是js开发的MCP Server可以发布在npm平台上,而如果是Python项目,则可以发布在pypi上。我们这里以pypi发布流程为例进行演示。pypi:https://pypi.org/
然后需要再次确认打包完成,并开启上传
# 回到项目主目录
# cd /root/MCP/pro_name
# 若此前打包完成,则无需再次打包
# python -m build
# 上传到pypi平台:pip install twine
python -m twine upload dist/*
上传需要在pypi平台获取一个API Token,获取方式,可参考:
https://blog.csdn.net/weixin_40988939/article/details/142235608
https://blog.csdn.net/weixin_42424046/article/details/136261145
pypi-AgEIcHlwaS5vcmcCJGJmNzQyYWM5LWZiZDAtNGNjNi04N2NmLTU3ZmM2NjQxZjg0OQACKlszLCJhZDk2YmVkMC01ODhjLTRmNWItYWQwYS1iMDMyZmRkNTc1OWMiXQAABiBXUS1pFQ6eTgcfvQnEegJsee_BHj4v_8sL7VmbFVG8Lw
上传完成后即可在pypi中搜索到包的信息:https://pypi.org/
然后即可在本地测试调用。这里以Cherry studio为例,尝试调用我们刚刚发布的pro_name包。点击添加服务器:

五、SSE传输方式的MCP服务器创建流程
以上MCP服务器都是stdio传输方式,而除此之外,目前MCP服务器还支持SSE传输和基于HTTP的流式传输。这两种传输方式也有非常广泛的实际用途,接下来详细介绍如何构建基于SSE和HTTP流式传输的MCP服务器。
1. stdio、SSE与基于HTTP的流式传输形式对比
1.1 MCP通信协议介绍
MCP(Model Context Protocol)是一种为了统一大规模模型和工具间通信而设计的协议,它定义了消息格式和通信方式。MCP 协议支持多种传输机制,其中包括 stdio、Server-Sent Events(SSE) 和 Streamable HTTP。每种通信方法在不同的应用场景中具有不同的优劣势,适用于不同的需求。
1.2 Stdio 传输(Standard Input/Output)
stdio 传输方式是最简单的通信方式,通常在本地工具之间进行消息传递时使用。它利用标准输入输出(stdin/stdout)作为数据传输通道,适用于本地进程间的交互。
- 工作方式:客户端和服务器通过标准输入输出流(stdin/stdout)进行通信。客户端向服务器发送命令和数据,服务器执行并通过标准输出返回结果。
- 应用场景:适用于本地开发、命令行工具、调试环境,或者模型和工具服务在同一进程内运行的情况。
1.3 Server-Sent Events(SSE)
SSE 是基于 HTTP 协议的流式传输机制,它允许服务器通过 HTTP 单向推送事件到客户端。SSE 适用于客户端需要接收服务器推送的场景,通常用于实时数据更新。
- 工作方式:客户端通过 HTTP GET 请求建立与服务器的连接,服务器以流式方式持续向客户端发送数据,客户端通过解析流数据来获取实时信息。
- 应用场景:适用于需要服务器主动推送数据的场景,如实时聊天、天气预报、新闻更新等。
1.4 Streamable HTTP
Streamable HTTP 是 MCP 协议中新引入的一种传输方式,它基于 HTTP 协议支持双向流式传输。与传统的 HTTP 请求响应模型不同,Streamable HTTP 允许服务器在一个长连接中实时向客户端推送数据,并且可以支持多个请求和响应的流式传输。
不过需要注意的是,MCP只提供了Streamable HTTP协议层的支持,也就是规范了MCP客户端在使用Streamable HTTP通信时的通信规则,而并没有提供相关的SDK客户端。开发者在开发Streamable HTTP机制下的客户端和服务器时,可以使用比如Python httpx库进行开发。
- 工作方式:客户端通过 HTTP POST 向服务器发送请求,并可以接收流式响应(如 JSON-RPC 响应或 SSE 流)。当请求数据较多或需要多次交互时,服务器可以通过长连接和分批推送的方式进行数据传输。
- 应用场景:适用于需要支持高并发、低延迟通信的分布式系统,尤其是跨服务或跨网络的应用。适合高并发的场景,如实时流媒体、在线游戏、金融交易系统等。
2. 基于SSE传输的MCP服务器创建流程
需要注意的是,目前MCP SDK只提供了stdio和SSE两种传输方式的开发库,而暂时还没有提供基于流式HTTP传输的开发工具。因此在进行MCP开发过程中,实现stdio和SSE传输方式较为简单,但要实现流式传输的HTTP流程则会非常复杂。这里我们先介绍相对简单的SSE传输方式的实现方法。当我们使用MCP Python SDK开发MCP服务器时,只需要在此处进行设置:
mcp.run(transport='sse')
即可让MCP服务器开启SSE模式,非常简单。这里我们以创建一个查询天气MCP服务器为例进行演示。
- 创建项目主目录
cd /root/autodl-tmp/MCP
mkdir ./MCP-sse-test
cd ./MCP-sse-test
- 创建基础项目结构
uv init mcp-get-weather
cd mcp-get-weather
# 创建虚拟环境
uv venv
# 激活虚拟环境
source .venv/bin/activate
uv add mcp httpx
然后删除主目录下的main.py文件,并创建代码文件夹:
mkdir -p ./src/mcp_get_weather
cd ./src/mcp_get_weather
-
创建服务器核心代码,其中server.py主要负责进行天气查询
-
创建服务器核心代码:在src/mcp_get_weather中创建三个代码文件:
__init__.py、__main__.py和server.py- 其中server.py主要负责进行天气查询。
-
采用了fastmcp进行创建,而在mcp.run中设置了使用sse方式进行传输。
mcp.run(transport='sse') -
此外需要在
__init__.py中写入from .server import main -
而在
__main__.py中写入:from mcp_get_weather import main main() -
同时回到主目录,修改项目配置文件
pyproject.toml:[build-system] requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta" [project] name = "mcp-get-weather" version = "0.1.5" description = "输入OpenWeather-API-KEY,获取天气信息。" readme = "README.md" requires-python = ">=3.9" dependencies = [ "httpx>=0.28.1", "mcp>=1.6.0", "openai>=1.75.0", "python-dotenv>=1.1.0", ] [project.scripts] mcp-get-weather = "mcp_get_weather:main" [tool.setuptools] package-dir = {"" = "src"} [tool.setuptools.packages.find] where = ["src"]
至此即完成了整个项目的代码编写工作!
3. 基于SSE的MCP服务器发布流程
关于上线发布,这里仍然考虑发布到pypi平台,并使用cherry studio进行本地调用测试。
- 打包上传:
# 回到项目主目录
# cd /root/autodl-tmp/MCP/MCP-sse-test/mcp-get-weather
uv pip install build twine
python -m build
python -m twine upload dist/*
- 查看发布的库:https://pypi.org/search/?q=mcp-get-weather
- 本地安装:
pip install mcp-get-weather
-
使用Cherry studio进行连接:即可使用Cherry studio连接SSE模式下的MCP服务器,这里只需要输入服务器地址即可:
六、HTTP流式传输
最后一部分,让我们来介绍基于HTTP流式传输的MCP服务器创建流程。需要注意的是,尽管HTTP流式传输是理论上性能最好的传输方式,但开发和应用的门槛都很大,不仅目前MCP SDK没有HTTP流式传输开发工具,而且目前主流的MCP客户端,如Cherry Studio和Cursor等,对于HTTP流式传输的MCP服务器调用支持都很差,因此目前对于MCP来说,HTTP流式传输功能还处于初级阶段。

浙公网安备 33010602011771号