Build Your First MCP Server in Python (Stateless Spec Edition)
TL;DR · AI 摘要
Build Your First MCP Server in Python Stateless Spec Edition - KDnuggets publ: 9-Oct, 2026 - Blog Top Posts About - Topi...
核心要点
- 主题聚焦:Build Your First MCP Server in Python Stateless
- 来源:KDnuggets,建议结合原文判断细节。
- AI 分析暂不可用,本条为保底评分与摘要。
使用 Python 构建你的第一个 MCP 服务器(无状态规范版)- KDnuggets
publ: 2026年10月9日
- 博客热门文章
- 主题 AI 职业建议 计算机视觉 数据工程 数据科学 语言模型 机器学习 MLOps NLP 编程 Python SQL
- 数据集
- 活动
- 资源 快速参考指南 推荐 技术简报
- 广告
加入新闻通讯
#header end
/ad_wrapper
使用 Python 构建你的第一个 MCP 服务器(无状态规范版)
MCP 现在变得更简单了。学习如何用 Python 构建一个无状态的 MCP 服务器,并通过 HTTP 暴露工具、资源和提示。
作者:
Kanwal Mehreen
,KDnuggets 技术编辑兼内容专家,2026年10月9日发布于
Python
<div class="addthis_native_toolbox"></div>
今年夏天,模型上下文协议发生了重要变化。根据 2026-07-28 的 MCP 规范,协议核心现在变为无状态:现代客户端在发出请求前不再需要建立协议会话,服务器也不再依赖 Mcp-Session-Id 处理常规请求。这使得 MCP 服务器更容易在常规 HTTP 基础设施后进行扩展。
同时,官方 Python SDK 已升级到 v2 稳定版本,提供了更高层次的 MCPServer API,可以通过常规 Python 函数定义工具、资源和提示。
在本教程中,我们将构建一个小型但完整的 MCP 服务器,通过 Streamable HTTP 运行它,在本地进行检查,并使用 Python MCP 客户端连接到它。让我们开始吧。
我们要构建什么?
我们将创建一个小型开发者知识库服务器。
它将暴露三个 MCP 原语:
Tool:
search_kb(query, limit)
Resource:
kb://articles
Prompt:
draft_support_reply(customer_message)该服务器将不包含任何用户会话状态。每个请求都将包含处理所需的所有信息,这使其成为新无状态 MCP 模型的良好示例。
概念上:
LLM 主机
|
| MCP 请求
v
+-----------------------+
| Python MCP 服务器 |
| |
| search_kb() |
| kb://articles |
| draft_support_reply() |
+-----------------------+第一步:创建项目
当前 Python SDK 要求 Python 3.10 或更高版本。官方文档建议安装 CLI 附加组件,因为它为我们提供了开发命令和 MCP 检查器工作流程。
使用 uv:
mkdir first-mcp-server
cd first-mcp-server
uv init
uv add "mcp[cli]"或使用 pip:
pip install "mcp[cli]"你的项目可以小到:
first-mcp-server/
├── server.py
└── client.py不需要任何框架样板代码。
第二步:创建你的第一个 MCP 服务器
创建 server.py:
from mcp.server import MCPServer
mcp = MCPServer(
"Developer Support KB",
instructions=(
"使用知识库工具回答支持问题。 "
"优先使用检索到的 KB 信息而不是猜测。"
),
)MCPServer 是当前 Python SDK 中的高级服务器 API。对于大多数服务器来说,这就是你想要的 API。SDK 还暴露了一个更底层的 Server 类,但该类仅适用于需要对模式、协议元数据或自定义方法进行精确控制的情况。
现在让我们为服务器添加一些数据。
ARTICLES = [
{
"id": "python-env",
"title": "Creating a Python virtual environment",
"body": (
"Create a virtual environment with `python -m venv .venv`, "
"then activate it before installing dependencies."
),
},
{
"id": "reset-password",
"title": "Resetting your password",
"body": (
"Open Account Settings, choose Security, and select "
"Reset Password. A verification email will be sent."
),
},
{
"id": "api-rate-limit",
"title": "Understanding API rate limits",
"body": (
"API rate limits restrict the number of requests allowed "
"within a time window. Clients should retry using "
"exponential backoff after receiving a rate-limit response."
),
},
]到目前为止这只是 Python。
有趣的部分是从我们通过 MCP 暴露函数开始。
第3步:添加 MCP 工具
MCP 工具是模型可以决定调用的函数。
将以下内容添加到 server.py 中:
@mcp.tool()
def search_kb(query: str, limit: int = 3) -> list[dict[str, str]]:
"""Search the support knowledge base.
Args:
query: Words or phrases to search for.
limit: Maximum number of articles to return.
"""
query = query.lower()
matches = []
for article in ARTICLES:
searchable_text = (
article["title"] + " " + article["body"]
).lower()
if query in searchable_text:
matches.append(article)
return matches[:limit]注意我们没有写的内容。
没有 JSON Schema。
没有手动编写的工具清单。
没有参数解析器。
SDK 会从 Python 函数本身推导出工具定义。其类型提示会成为 MCP 输入模式,而类似以下的默认值:
limit: int = 3会使参数在生成的模式中变为可选。官方 SDK 文档使用的就是这种模式。从概念上讲,你的函数:
def search_kb(
query: str,
limit: int = 3
)会变成类似以下的结构:
{
"name": "search_kb",
"inputSchema": {
"type": "object",
"properties": {
"query": {
"type": "string"
},
"limit": {
"type": "integer",
"default": 3
}
},
"required": ["query"]
}
}这就是为什么在 Python 中使用 MCP 开发感觉非常普通的原因:你的函数签名实际上就是你的接口定义。
第4步:添加资源
工具是模型可以调用的操作。
资源则不同。它们会暴露主机应用程序可以加载到上下文中的信息。
添加以下内容:
@mcp.resource("kb://articles")
def list_articles() -> str:
"""Return the available knowledge-base articles."""
lines = []
for article in ARTICLES:
lines.append(
f"{article['id']}: {article['title']}"
)
return "\n".join(lines)该资源具有以下 URI:
kb://articles客户端无需调用工具即可读取它。
Resource ≈ 可读取的数据
Tool ≈ 可执行操作的函数SDK 文档大致将资源与 GET 类行为进行比较,将工具与以操作为导向的 POST 类行为进行比较。
第5步:添加 MCP 提示
我们还可以暴露一个可重用的提示模板。
@mcp.prompt()
def draft_support_reply(customer_message: str) -> str:
"""创建用于起草简洁支持回复的提示。"""
return f"""
你是一名技术支持助理。
针对以下客户消息撰写简洁且有帮助的回复:
{customer_message}
在相关时使用支持知识库。
不要编造产品政策。
""".strip()这只是一个带有装饰器的 Python 函数。
提示通常由用户或主机发起,而不是由模型自主调用。当前 SDK 通过相同的装饰器导向服务器接口支持工具、资源和提示。
此时,server.py 的内容如下:
from mcp.server import MCPServer
mcp = MCPServer(
"Developer Support KB",
instructions=(
"使用知识库工具回答支持问题。 "
"优先使用检索到的知识库信息,而非猜测。"
),
)
ARTICLES = [
{
"id": "python-env",
"title": "创建 Python 虚拟环境",
"body": (
"使用 `python -m venv .venv` 创建虚拟环境, "
"然后在安装依赖项前激活它。"
),
},
{
"id": "reset-password",
"title": "重置密码",
"body": (
"打开账户设置,选择安全选项,然后选择 "
"重置密码。将发送验证邮件。"
),
},
{
"id": "api-rate-limit",
"title": "理解 API 速率限制",
"body": (
"API 速率限制限制了在特定时间窗口内允许的请求数量。 "
"客户端在收到速率限制响应后,应使用 "
"指数退避策略进行重试。"
),
},
]
@mcp.tool()
def search_kb(
query: str,
limit: int = 3,
) -> list[dict[str, str]]:
"""搜索支持知识库。"""
query = query.lower()
matches = []
for article in ARTICLES:
searchable_text = (
article["title"] + " " + article["body"]
).lower()
if query in searchable_text:
matches.append(article)
return matches[:limit]
@mcp.resource("kb://articles")
def list_articles() -> str:
"""返回可用的知识库文章。"""
return "\n".join(
f"{article['id']}: {article['title']}"
for article in ARTICLES
)
@mcp.prompt()
def draft_support_reply(
customer_message: str,
) -> str:
"""创建支持回复提示。"""
return f"""
你是一名技术支持助理。
针对以下客户消息撰写简洁且有帮助的回复:
{customer_message}
在相关时使用支持知识库。
不要编造产品政策。
""".strip()
if __name__ == "__main__":
mcp.run("streamable-http")这是一个完整的可通过网络访问的 MCP 应用程序。
第 6 步:在开发模式下运行
在开发过程中,SDK 提供了一个便捷命令:
uv run mcp dev server.pyMCP 开发命令会启动带有 MCP Inspector 支持的服务器,为您提供一个用于列出和调用工具的用户界面。官方 SDK 建议将此作为基本开发流程。
打开终端中打印的 Inspector URL。
你应该看到:
search_kb在工具下。
尝试使用以下内容调用它:
{
"query": "rate limit"
}结果应包含:
[
{
"id": "api-rate-limit",
"title": "理解API速率限制",
"body": "API速率限制限制了..."
}
]现在你已经拥有一个可用的MCP服务器。
第7步:通过可流式HTTP运行
要实际运行HTTP服务器,请执行:
uv run python server.py默认情况下,你的MCP端点暴露在:
http://127.0.0.1:8000/mcpSDK的可流式HTTP服务器使用/mcp作为其默认端点。
如果你想将其作为普通ASGI应用程序运行,请将__main__块替换为:
app = mcp.streamable_http_app()然后使用Uvicorn启动:
uvicorn server:app当MCP是FastAPI或Starlette部署中的一个组件时,这尤其有用。streamable_http_app()返回一个标准的Starlette兼容ASGI应用。
无状态MCP更新中发生了哪些变化?
这需要特别关注,因为目前许多在线MCP教程描述的都是较旧的生命周期。在协议的旧版本中,HTTP客户端实际上执行了以下操作:
Client
|
| 初始化
v
Server
|
| Mcp-Session-Id
v
Client
|
| 后续请求 + 会话ID
v
相同逻辑会话这创建了一个需要粘性路由或共享会话基础设施的多实例部署。2026-07-28的协议变更解决了这个问题。
现代MCP请求被设计为自包含:
请求1
|
v
Server A
请求2
|
v
Server C
请求3
|
v
Server B不需要协议会话将这些调用关联起来。MCP团队明确描述这是从双向、有状态协议核心转向无状态请求/响应模型。这使得普通负载均衡变得容易得多。
编写Python客户端
让我们在不依赖第三方AI应用的情况下验证服务器。
创建client.py:
import asyncio
from mcp import Client
async def main() -> None:
async with Client(
"http://127.0.0.1:8000/mcp"
) as client:
print(
"协议:",
client.protocol_version,
)
tools = await client.list_tools()
print("\n可用工具:")
for tool in tools.tools:
print("-", tool.name)
result = await client.call_tool(
"search_kb",
{
"query": "rate limit",
"limit": 2,
},
)
print("\n工具结果:")
if result.structured_content:
print(result.structured_content)
else:
print(result.content)
if __name__ == "__main__":
asyncio.run(main())在终端中运行服务器:
然后在另一个终端中运行客户端:
uv run python client.pyv2客户端可以直接接受HTTP URL并自动使用可流式HTTP。它还会暴露协商的协议版本,因此使用当前的客户端/服务器对时,你应该能看到连接报告的现代协议版本。
你的输出应该类似于我的:
协议: 2026-07-28
可用工具:
- search_kb
{'result': [{'id': 'api-rate-limit', 'title': '理解API速率限制', 'body': 'API速率限制限制了在时间窗口内允许的请求数量。客户端在收到速率限制响应后应使用指数退避重试。'}]}但如果我的应用实际上需要状态呢?
"无状态协议"并不意味着你的应用永远无法维护状态。
它的含义是,MCP本身不再在协议会话内部隐藏应用状态。
假设你正在构建一个购物服务器。不再依赖:
MCP session 42 owns this basket而是可以暴露:
@mcp.tool()
def create_basket() -> dict[str, str]:
basket_id = create_new_basket()
return {
"basket_id": basket_id
}之后:
@mcp.tool()
def add_item(
basket_id: str,
product_id: str,
) -> dict:
return add_product(
basket_id,
product_id,
)现在模型会显式地看到并传递:
basket_idMCP维护者特别推荐在新的无状态协议下,对应用层状态使用这种显式处理模式。
这是一个微妙但有用的架构转变:
旧理念:
协议记住状态
新理念:
应用拥有状态
标识符显式传递扩展服务器
无状态核心在部署多个工作进程时变得特别有价值。
例如:
uvicorn server:app --workers 4现代MCP请求可以由任何工作进程处理,因为协议不再要求它必须返回处理过先前请求的工作进程。
+--> Worker 1
Client --> LB +--> Worker 2
+--> Worker 3
+--> Worker 4对于多轮交互、共享订阅事件、授权和分布式状态等高级功能,还有其他需要考虑的因素,但这些属于应用架构问题,而不是基本MCP工具执行的强制要求。
总结
这里的实际变化比规范差异看起来要小:你仍然使用@mcp.tool()装饰函数,仍然返回字典并让框架构建结果,仍然通过mcp.run()来运行。不同的是底层实现。你不再需要任何握手协商,不需要保持会话温暖,也不需要配置粘性路由。熟悉MCPServer,保持标准输出整洁,其余无状态规范大部分都会远离你的视线。
Kanwal Mehreen 是一名机器学习工程师和技术作家,对数据科学以及人工智能与医学的交叉领域充满热情。她合著了电子书《用ChatGPT最大化生产力》。作为2022年APAC地区谷歌新一代学者,她倡导多样性和学术卓越。她还被授予Teradata科技多样性学者、Mitacs Globalink研究学者和哈佛WeCode学者称号。Kanwal是变革的坚定倡导者,她创立了FEMCodes来赋能STEM领域的女性。
更多相关内容
- 用MCP服务器控制你的Spotify播放列表
- 构建一个简单的MCP服务器
- 7个简单步骤构建你的第一个VS Code Python扩展
- FastMCP:用Python方式构建MCP服务器和客户端
- KDnuggets每周精选:构建并部署你的第一个自主...
- 入门Smolagents:15分钟构建你的第一个代码代理
<hr class="grey-line"><br> <div><h3>我们推荐的5门免费课程</h3><br> </div>
Mailchimp for WordPress v4.14.1 - https://wordpress.org/plugins/mailchimp-for-wp/
/ Mailchimp for WordPress插件
你可以从这里开始编辑。
如果评论已关闭。
<= 上一篇
#content end
<script type="text/javascript">kda_sid_write(kda_sid_n);</script>
最新文章
/think
- 使用 Python 构建首个 MCP 服务器(无状态规范版) 10 款免费 AI 工具替代数据科学家的昂贵软件 企业级 AI 中算法设计与监管标准的衔接 使用 Docker 构建 AI 代理 了解自我进化的 AI 代理的 7 个最佳学习资源 OpenAI 点评:数据科学家的现实检验
热门文章
- 工程师的 Python 基础:KDnuggets 快速参考指南
- 10 个让代码更简洁高效的 Python 一行式写法
- 我测试了 5 款 AI 编码助手一个月:实际发现的真相
- Meta Muse 解析:是什么?如何运作?能做什么?
- 2026 年可使用的 5 款免费大语言模型 API 提供商
- 5 门免费课程学习 AI 工程
- 构建健壮 Python AI 库的 5 个最佳实践
- 使用 Ollama 管理本地语言模型:KDnuggets 快速参考指南
- 如何使用 Marimo 进行交互式数据分析
- 前置部署工程师:AI 最热门的新职业,还是换了个更好听的咨询头衔?
#content_wrapper end
© 2026
Guiding Tech Media
|
关于
联系我们
广告合作
隐私政策
服务条款
发布于 2026 年 10 月 9 日 by
blank
不,谢谢!
/.main_wrapper
<script defer type="text/javascript" src="https://s7.addthis.com/js/300/addthis_widget.js#pubid=gpsaddthis"></script>
noptimize
/noptimize