第九课

MCP 从零实现教程(九)

第九课:MCP 生命周期(Lifecycle)——Client 第一次连接到底发生了什么?

目标:理解一个 MCP Server 从启动到正式工作的完整过程。


📚 本课目标

学完本课,你将能够回答:

  • 为什么不能一连接就调用 Tool?
  • initialize 到底是干什么的?
  • 什么是 Capability(能力协商)?
  • 为什么官方要设计一个初始化阶段?

这一课没有太多复杂代码,但它是理解 MCP 协议 的关键。


📖 第一部分:回顾我们的 Mini MCP

前几课,我们一直这样:

Client
    │
    ▼
tools/list
    │
    ▼
Server

或者:

Client
    │
    ▼
tools/call
    │
    ▼
Server

是不是感觉很自然?

但是这里其实少了一步。


📖 第二部分:现实世界不能这样工作

假设:

Cursor 第一次连接你的 MCP Server。

它什么都不知道。

不知道:

  • 你支持哪个 MCP 版本?
  • 你有没有 Tool?
  • 有没有 Resource?
  • 有没有 Prompt?
  • 有没有 Logging?

所以:

第一件事情不是:

tools/list

而是:

你好,我们先认识一下。

这就是:

initialize

官方规定:initialize 必须是 Client 与 Server 的第一次交互,双方在这里完成协议版本和能力协商。 


📖 第三部分:真正的生命周期

真正 MCP:

Client

│

▼

initialize

│

▼

initialize Response

│

▼

initialized

│

▼

tools/list

│

▼

tools/call

│

▼

resources/list

│

▼

......

注意:

这里有三个阶段。

很多教程都会漏掉:

initialized

📖 第四部分:第一步——Client 发送 initialize

第一次连接:

发送:

{
    "jsonrpc": "2.0",

    "id": 1,

    "method": "initialize",

    "params": {

        "protocolVersion": "2025-06-18",

        "clientInfo": {

            "name": "MiniClient",

            "version": "1.0"

        },

        "capabilities": {}

    }

}

是不是像登录一样?

这里告诉 Server:

我是:

MiniClient

我支持:

2025-06-18

我支持这些能力:

...

📖 第五部分:Server 回复

Server:

返回:

{
    "jsonrpc": "2.0",

    "id": 1,

    "result": {

        "protocolVersion": "2025-06-18",

        "serverInfo": {

            "name": "MiniServer",

            "version": "1.0"

        },

        "capabilities": {

            "tools": {},

            "resources": {},

            "prompts": {}

        }

    }

}

这里:

Server 告诉 Client:

我是:

MiniServer

我支持:

Tool

Resource

Prompt

📖 第六部分:什么叫 Capability(能力)?

很多人第一次看到:

"capabilities": {

    "tools": {},

    "resources": {},

    "prompts": {}

}

觉得:

就是配置。

其实不是。

它真正表示:

Server 能做什么。

例如:

如果:

{

    "tools": {}

}

说明:

支持 Tool

如果没有:

{

    "resources": {}

}

说明:

不能读取 Resource

所以:

Capability:

其实就是:

Feature List(功能列表)

💻 第七部分:我们自己实现 initialize()

开始写。

def initialize(request):

    return {

        "jsonrpc": "2.0",

        "id": request["id"],

        "result": {

            "protocolVersion": "1.0",

            "serverInfo": {

                "name": "MiniMCP",

                "version": "0.1"

            },

            "capabilities": {

                "tools": {},

                "resources": {},

                "prompts": {}

            }

        }

    }

是不是非常简单?


💻 第八部分:Dispatcher 增加 initialize

以前:

Dispatcher:

if method == "tools/list":

今天:

增加:

def dispatch(request):

    method = request["method"]

    if method == "initialize":

        return initialize(request)

    elif method == "tools/list":

        return list_tools()

    elif method == "tools/call":

        return call_tool(request)

这样:

Server:

已经支持:

初始化。


📖 第九部分:为什么还有 initialized?

很多教程都会讲到这里。

其实:

还差一步。

Client:

收到:

initialize Response

以后:

必须发送:

{
    "jsonrpc":"2.0",

    "method":"notifications/initialized"
}

注意:

没有:

id

因为:

它不是 Request。

它只是:

通知。

意思就是:

我已经准备好了。

开始正式工作。

官方规范要求:Server 在收到 notifications/initialized 之前,不应开始发送普通请求;Client 也应在初始化完成后再进入正常通信阶段。 


💻 第十部分:增加 initialized

我们自己实现。

server_state = {

    "initialized": False

}

增加:

def initialized():

    server_state["initialized"] = True

    print("客户端初始化完成")

Dispatcher:

增加:

elif method == "notifications/initialized":

    initialized()

    return None

是不是:

非常简单?


📖 第十一部分:整个生命周期终于完整了

现在:

真正流程:

                Client

                   │

                   ▼

             initialize

                   │

                   ▼

        Server 返回能力

                   │

                   ▼

 notifications/initialized

                   │

                   ▼

      Server 标记:

      initialized=True

                   │

                   ▼

            tools/list

                   │

                   ▼

            tools/call

                   │

                   ▼

             正常工作

看到这里,

是不是终于理解:

为什么:

官方把:

initialize

单独设计成一个协议?

因为:

这是:

整个连接建立的开始。


🧪 本课练习

请自己新增:

server_state = {

    "initialized": False

}

要求:

第一次:

发送:

{

    "method":"initialize"

}

输出:

Server 信息。

然后:

发送:

{

    "method":"notifications/initialized"

}

输出:

Client Ready

最后:

发送:

{

    "method":"tools/list"

}

能够正常工作。

你还可以进一步改造 Dispatcher:如果 server_state["initialized"]False,除了 initialize 之外的请求全部拒绝,这样就更接近真实 MCP 的行为。


🎯 本课总结

今天,我们终于完成了 MCP 的完整生命周期。

一个真实的 MCP Server 启动以后,不是马上执行 Tool。

而是:

启动

↓

initialize(握手)

↓

能力协商(Capabilities)

↓

initialized(准备完成)

↓

正式开始工作

↓

tools/list

↓

tools/call

↓

......

↓

关闭连接

这也是为什么 MCP 看起来比普通 REST API 多了一层流程:它不仅要调用接口,还要先建立一个协议会话,确认双方都支持哪些能力、使用哪个协议版本,再进入正常通信。 


🚀 第十课预告

从第十课开始,我们不再学习单个协议点,而是开始重构整个项目

我们会把前九课零散的代码整理成一个真正的项目结构:

mini_mcp/
│
├── server.py
├── dispatcher.py
├── registry.py
├── tools/
│   ├── math.py
│   ├── weather.py
│   └── file.py
├── resources/
├── prompts/
└── client.py

从这一课开始,你写的将不再是”教程代码”,而是一个可以持续扩展、不断接近官方 MCP Server 的真实项目。

posted @ 2026-07-31 18:02  zwx901323  阅读(3)  评论(0)    收藏  举报