\n\n### 最新文章\n\n\n /think\n\n- 使用 Python 构建首个 MCP 服务器(无状态规范版) 10 款免费 AI 工具替代数据科学家的昂贵软件 企业级 AI 中算法设计与监管标准的衔接 使用 Docker 构建 AI 代理 了解自我进化的 AI 代理的 7 个最佳学习资源 OpenAI 点评:数据科学家的现实检验\n\n## 热门文章\n\n- 工程师的 Python 基础:KDnuggets 快速参考指南\n\n- 10 个让代码更简洁高效的 Python 一行式写法\n\n- 我测试了 5 款 AI 编码助手一个月:实际发现的真相\n\n- Meta Muse 解析:是什么?如何运作?能做什么?\n\n- 2026 年可使用的 5 款免费大语言模型 API 提供商\n\n- 5 门免费课程学习 AI 工程\n\n- 构建健壮 Python AI 库的 5 个最佳实践\n\n- 使用 Ollama 管理本地语言模型:KDnuggets 快速参考指南\n\n- 如何使用 Marimo 进行交互式数据分析\n\n- 前置部署工程师:AI 最热门的新职业,还是换了个更好听的咨询头衔?\n\n#content_wrapper end\n\n© 2026\n\nGuiding Tech Media\n\n|\n\n关于\n\n联系我们\n\n广告合作\n\n隐私政策\n\n服务条款\n\n发布于 2026 年 10 月 9 日 by\n\nblank\n\n不,谢谢!\n\n/.main_wrapper\n\n\n\nnoptimize\n\n/noptimize","speakable":{"@type":"SpeakableSpecification","cssSelector":["[data-speakable='summary']","[data-speakable='takeaways']"]},"author":{"@type":"Organization","name":"KDnuggets","url":"https://www.kdnuggets.com"},"publisher":{"@type":"Organization","name":"traeai","url":"https://www.traeai.com","logo":{"@type":"ImageObject","url":"https://www.traeai.com/icon.svg"},"sameAs":["https://github.com/cq0206/traeai"]},"mainEntityOfPage":"https://www.traeai.com/articles/5c6dcb6e-43d8-4321-a1e5-1edd7a637d56"},{"@type":"WebSite","@id":"https://www.traeai.com/#website","url":"https://www.traeai.com","name":"traeai","description":"traeai 为开发者、研究员和内容团队筛选高质量 AI 技术内容,提供摘要、评分、趋势雷达与知识库产出。","inLanguage":["zh-CN","en"],"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://www.traeai.com/?q={search_term_string}"},"query-input":"required name=search_term_string"}},{"@type":"BreadcrumbList","@id":"https://www.traeai.com/articles/5c6dcb6e-43d8-4321-a1e5-1edd7a637d56#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"首页","item":"https://www.traeai.com/"},{"@type":"ListItem","position":2,"name":"文章","item":"https://www.traeai.com/brief"},{"@type":"ListItem","position":3,"name":"KDnuggets","item":"https://www.traeai.com/sources"},{"@type":"ListItem","position":4,"name":"Build Your First MCP Server in Python (Stateless Spec Edition)","item":"https://www.traeai.com/articles/5c6dcb6e-43d8-4321-a1e5-1edd7a637d56"}]}]}
KDnuggets

Build Your First MCP Server in Python (Stateless Spec Edition)

6.9内容质量

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 分析暂不可用,本条为保底评分与摘要。
#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 原语:

code
Tool:
    search_kb(query, limit)

Resource:
    kb://articles

Prompt:
    draft_support_reply(customer_message)

该服务器将不包含任何用户会话状态。每个请求都将包含处理所需的所有信息,这使其成为新无状态 MCP 模型的良好示例。

概念上:

code
LLM 主机
   |
   | MCP 请求
   v
+-----------------------+
| Python MCP 服务器     |
|                       |
| search_kb()           |
| kb://articles         |
| draft_support_reply() |
+-----------------------+

第一步:创建项目

当前 Python SDK 要求 Python 3.10 或更高版本。官方文档建议安装 CLI 附加组件,因为它为我们提供了开发命令和 MCP 检查器工作流程。

使用 uv:

code
mkdir first-mcp-server
cd first-mcp-server

uv init
uv add "mcp[cli]"

或使用 pip:

code
pip install "mcp[cli]"

你的项目可以小到:

code
first-mcp-server/
├── server.py
└── client.py

不需要任何框架样板代码。

第二步:创建你的第一个 MCP 服务器

创建 server.py:

code
from mcp.server import MCPServer

mcp = MCPServer(
    "Developer Support KB",
    instructions=(
        "使用知识库工具回答支持问题。 "
        "优先使用检索到的 KB 信息而不是猜测。"
    ),
)

MCPServer 是当前 Python SDK 中的高级服务器 API。对于大多数服务器来说,这就是你想要的 API。SDK 还暴露了一个更底层的 Server 类,但该类仅适用于需要对模式、协议元数据或自定义方法进行精确控制的情况。

现在让我们为服务器添加一些数据。

code
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 中:

code
@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 输入模式,而类似以下的默认值:

code
limit: int = 3

会使参数在生成的模式中变为可选。官方 SDK 文档使用的就是这种模式。从概念上讲,你的函数:

code
def search_kb(
    query: str,
    limit: int = 3
)

会变成类似以下的结构:

code
{
  "name": "search_kb",
  "inputSchema": {
    "type": "object",
    "properties": {
      "query": {
        "type": "string"
      },
      "limit": {
        "type": "integer",
        "default": 3
      }
    },
    "required": ["query"]
  }
}

这就是为什么在 Python 中使用 MCP 开发感觉非常普通的原因:你的函数签名实际上就是你的接口定义。

第4步:添加资源

工具是模型可以调用的操作。

资源则不同。它们会暴露主机应用程序可以加载到上下文中的信息。

添加以下内容:

code
@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:

code
kb://articles

客户端无需调用工具即可读取它。

code
Resource ≈ 可读取的数据
Tool     ≈ 可执行操作的函数

SDK 文档大致将资源与 GET 类行为进行比较,将工具与以操作为导向的 POST 类行为进行比较。

第5步:添加 MCP 提示

我们还可以暴露一个可重用的提示模板。

code
@mcp.prompt()
def draft_support_reply(customer_message: str) -> str:
    """创建用于起草简洁支持回复的提示。"""

    return f"""
你是一名技术支持助理。

针对以下客户消息撰写简洁且有帮助的回复:

{customer_message}

在相关时使用支持知识库。
不要编造产品政策。
""".strip()

这只是一个带有装饰器的 Python 函数。

提示通常由用户或主机发起,而不是由模型自主调用。当前 SDK 通过相同的装饰器导向服务器接口支持工具、资源和提示。

此时,server.py 的内容如下:

code
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 提供了一个便捷命令:

code
uv run mcp dev server.py

MCP 开发命令会启动带有 MCP Inspector 支持的服务器,为您提供一个用于列出和调用工具的用户界面。官方 SDK 建议将此作为基本开发流程。

打开终端中打印的 Inspector URL。

你应该看到:

code
search_kb

在工具下。

尝试使用以下内容调用它:

code
{
  "query": "rate limit"
}

结果应包含:

code
[
  {
    "id": "api-rate-limit",
    "title": "理解API速率限制",
    "body": "API速率限制限制了..."
  }
]

现在你已经拥有一个可用的MCP服务器。

第7步:通过可流式HTTP运行

要实际运行HTTP服务器,请执行:

code
uv run python server.py

默认情况下,你的MCP端点暴露在:

code
http://127.0.0.1:8000/mcp

SDK的可流式HTTP服务器使用/mcp作为其默认端点。

如果你想将其作为普通ASGI应用程序运行,请将__main__块替换为:

code
app = mcp.streamable_http_app()

然后使用Uvicorn启动:

code
uvicorn server:app

当MCP是FastAPI或Starlette部署中的一个组件时,这尤其有用。streamable_http_app()返回一个标准的Starlette兼容ASGI应用。

无状态MCP更新中发生了哪些变化?

这需要特别关注,因为目前许多在线MCP教程描述的都是较旧的生命周期。在协议的旧版本中,HTTP客户端实际上执行了以下操作:

code
Client
  |
  | 初始化
  v
Server
  |
  | Mcp-Session-Id
  v
Client
  |
  | 后续请求 + 会话ID
  v
相同逻辑会话

这创建了一个需要粘性路由或共享会话基础设施的多实例部署。2026-07-28的协议变更解决了这个问题。

现代MCP请求被设计为自包含:

code
请求1
   |
   v
Server A

请求2
   |
   v
Server C

请求3
   |
   v
Server B

不需要协议会话将这些调用关联起来。MCP团队明确描述这是从双向、有状态协议核心转向无状态请求/响应模型。这使得普通负载均衡变得容易得多。

编写Python客户端

让我们在不依赖第三方AI应用的情况下验证服务器。

创建client.py:

code
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())

在终端中运行服务器:

然后在另一个终端中运行客户端:

code
uv run python client.py

v2客户端可以直接接受HTTP URL并自动使用可流式HTTP。它还会暴露协商的协议版本,因此使用当前的客户端/服务器对时,你应该能看到连接报告的现代协议版本。

你的输出应该类似于我的:

code
协议: 2026-07-28

可用工具:
- search_kb
  
  {'result': [{'id': 'api-rate-limit', 'title': '理解API速率限制', 'body': 'API速率限制限制了在时间窗口内允许的请求数量。客户端在收到速率限制响应后应使用指数退避重试。'}]}

但如果我的应用实际上需要状态呢?

"无状态协议"并不意味着你的应用永远无法维护状态。

它的含义是,MCP本身不再在协议会话内部隐藏应用状态。

假设你正在构建一个购物服务器。不再依赖:

code
MCP session 42 owns this basket

而是可以暴露:

code
@mcp.tool()
def create_basket() -> dict[str, str]:
    basket_id = create_new_basket()

    return {
        "basket_id": basket_id
    }

之后:

code
@mcp.tool()
def add_item(
    basket_id: str,
    product_id: str,
) -> dict:
    return add_product(
        basket_id,
        product_id,
    )

现在模型会显式地看到并传递:

code
basket_id

MCP维护者特别推荐在新的无状态协议下,对应用层状态使用这种显式处理模式。

这是一个微妙但有用的架构转变:

code
旧理念:
协议记住状态

新理念:
应用拥有状态
标识符显式传递

扩展服务器

无状态核心在部署多个工作进程时变得特别有价值。

例如:

code
uvicorn server:app --workers 4

现代MCP请求可以由任何工作进程处理,因为协议不再要求它必须返回处理过先前请求的工作进程。

code
+--> 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