前面三篇,我已经把 MCP 的认知和底层原理拆清楚了:

  1. MCP 是什么
  2. MCP 的运行流程
  3. 为什么 LLM 会调用 MCP Tool

但如果只停留在理解层面,其实没有意义。

作为开发者,我必须真正写一个 MCP,才算真正掌握。

这一篇,我会做两件事:

  • 用 Python 开发一个最小 MCP Server
  • 用 Python 模拟 Client 调用 MCP

虽然我本身是 Java 开发者,但我发现:

只要理解了 Python 版本的运行机制,我就能很容易迁移到 Java。

因为:

MCP 的核心是协议思想,而不是语言。


一、这一篇到底要解决什么?

很多教程的问题是:

  • 上来就贴代码
  • 但根本不知道代码为什么这么写

所以这一篇,我不会只讲“怎么写”,还会讲:

每一行代码在 MCP 体系里到底对应什么角色。


二、先明确 MCP 开发的本质

一句话:

MCP 开发,本质就是“把普通函数暴露成 AI 可调用能力”

例如:

def get_weather(city):
    return "晴天"

普通函数只能被程序调用。

但 MCP 做了一件事:

把它变成“LLM 可以发现和调用的 Tool”


三、MCP 的整体结构(必须先理解)

我先把完整链路画出来:

LLM
 ↓
MCP Client
 ↓
MCP Server
 ↓
Tool(Python函数)

每层在干什么?

层级 作用
LLM 决定调用什么
MCP Client 发送请求
MCP Server 管理 Tool
Tool 真正执行逻辑

四、开始开发:安装 MCP SDK

先安装 Python SDK:

pip install mcp

五、开发第一个 MCP Server(核心)

1. 创建 server.py

from mcp.server import Server

server = Server("weather-server")

这一段在做什么?

这里不是简单创建对象。

而是在做:

启动一个“能力服务”

此时 Server 内部会维护:

  • Tool 列表
  • Tool Schema
  • 请求路由

六、注册第一个 Tool

@server.tool()
def get_weather(city: str) -> str:
    """根据城市查询天气"""

    return f"{city}今天晴天,25度"

七、这里到底发生了什么?(重点)

很多人会觉得:

“不就是一个装饰器吗?”

但实际上:

这一行是整个 MCP 的核心。


MCP 内部做了什么?

1. 收集函数信息

MCP 会自动读取:

  • 函数名
  • 参数类型
  • 返回值类型
  • docstring

2. 自动生成 Tool Schema

类似:

{
  "name": "get_weather",
  "description": "根据城市查询天气",
  "parameters": {
    "city": "string"
  }
}

3. Tool 注册到 Server

Server 内部会维护:

Tool Name -> Python Function

类似:

get_weather -> 内存地址

八、为什么 LLM 后面能调用它?

这里终于进入核心。

注意:

LLM 从来没有“看到 Python 代码”

它看到的只有:

{
  "name": "get_weather",
  "description": "根据城市查询天气"
}

本质是什么?

Tool 被“翻译成文本描述”

然后:

  • Client 会把这些 Tool 信息塞进 Prompt
  • LLM 再决定是否调用

九、启动 MCP Server

if __name__ == "__main__":
    server.run()

十、server.run() 到底干了什么?

很多教程不会讲这一层,但这一层非常关键。


本质上:

server.run() 启动了一个 MCP 通信服务

它会:

  • 等待 Client 请求
  • 接收 Tool 调用
  • 分发到对应 Python 函数

类比 Java

如果我是 Java 开发者,我可以这样理解:

SpringBoot Controller
       ↓
接收 HTTP 请求
       ↓
调用 Service

MCP Server 本质类似:

MCP Request
      ↓
Tool Router
      ↓
Python Function

十一、开始开发 MCP Client

现在我再写一个 Client。


创建 client.py

from mcp.client import Client

client = Client("demo-client")

连接 MCP Server

client.connect("http://localhost:8000")

十二、连接时到底发生了什么?

这里不是简单 TCP 连接。

实际上:

Client 会开始“能力发现”


它会请求:

请告诉我你有哪些 Tool

Server 返回:

[
  {
    "name": "get_weather",
    "description": "根据城市查询天气"
  }
]

十三、LLM 为什么知道调用哪个 Tool?

现在:

  • 用户输入:
帮我查一下北京天气

Client 会构造 Prompt:

你可以使用以下工具:

工具:
get_weather
作用:根据城市查询天气

然后 LLM 推理:

它发现:

  • “天气”
  • “get_weather”

高度匹配。

于是输出:

{
  "tool": "get_weather",
  "arguments": {
    "city": "北京"
  }
}

十四、为什么参数是 JSON?

因为:

Tool Schema 已经提前定义了参数结构。

例如:

{
  "city": "string"
}

LLM 本质是在:

“按 Schema 填空”

而不是:

“凭空创造 JSON”


十五、Client 如何真正调用 Tool?

现在 Client 拿到:

{
  "tool": "get_weather",
  "arguments": {
    "city": "北京"
  }
}

Client 会做什么?

1. 解析 JSON

2. 发送 MCP 请求

类似:

调用 get_weather
参数 city=北京

Server 收到后:

根据:

get_weather -> Python函数

找到真实函数:

get_weather("北京")

十六、最终结果怎么回到 LLM?

Tool 返回:

北京今天晴天,25度

Client 再把结果塞回 Prompt:

工具返回:
北京今天晴天,25度

然后 LLM 再生成最终回答:

北京今天天气晴朗,气温25度。

十七、如果我是 Java 开发者,该怎么理解?

这一段非常重要。

很多 Java 开发者看到 Python 会慌。

但实际上:

MCP 和语言关系不大。


Java 对应关系

MCP Python Java 思维
Tool Service 方法
Server SpringBoot 服务
Schema DTO / JSON Schema
Client Feign / SDK
Tool Router Dispatcher

本质上:

MCP 就是“AI 时代的 RPC/接口调用系统”


十八、真正重要的收获(不是代码)

这一篇最重要的不是:

  • 会不会写 Python

而是:

我终于理解了 MCP 的本质机制。


MCP 真正做的事情

1. 把函数暴露成 Tool

2. 把 Tool 转成文本描述

3. 把文本描述交给 LLM

4. LLM 输出结构化调用

5. Client 转换成真实函数执行


十九、总结

我把这一篇压缩成一句话:

MCP 本质是“函数调用”和“自然语言”之间的翻译层

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐