第二课

很好,从这一课开始,我们统一采用 Markdown 教程 的形式。

以后每一课都会包含:

📚 本课目标
📖 本课知识
💻 动手实践
🧪 运行结果
📝 课后练习
🎯 本课总结
➡️ 下一课预告

尽量做到:

  • 代码 80%
  • 概念 20%
  • 每节课都可以直接复制运行
  • 每节课学完都能理解 MCP 底层原理,而不是学 SDK

MCP 从零实现教程(二)

第二课:自己实现 @tool 装饰器

目标:自己实现 MCP Tool 注册机制。

📚 本课目标

学完本课后,你应该能够理解:

  • 什么是 Tool Registry(工具注册中心)
  • Python 装饰器到底做了什么
  • 为什么 @mcp.tool() 一点都不神奇
  • 如何自己实现一个工具注册器

今天仍然不使用任何 MCP SDK。

官方 MCP SDK 的 @mcp.tool() 底层也是围绕 Python 函数、装饰器和反射来组织工具信息,只是在此基础上增加了参数 Schema、协议通信等能力。

📖 第一部分:为什么需要 Tool Registry?

上一课,我们写的是:

def add(a, b):
return a + b
def sub(a, b):
return a - b

调用方式:

print(add(1, 2))

如果工具越来越多:

add()
sub()
weather()
search()
translate()
send_email()
...

难道要写:

if tool == "add":
...
elif tool == "sub":
...
elif tool == "weather":
...

当然不行。

所以需要一个统一管理工具的地方。

例如:

TOOLS = {}

以后所有工具都放进去。

📖 第二部分:创建 Tool Registry

新建一个文件:

registry.py

写入:

TOOLS = {}

目前什么都没有。

打印:

print(TOOLS)

输出:

{}

📖 第三部分:自己实现 @tool

继续写:

TOOLS = {}
def tool(func):
print("注册工具:", func.name)
TOOLS[func.name] = func
return func

是不是很简单?

这里只有三件事情:

收到函数

保存函数

返回函数

📖 第四部分:使用装饰器

下面开始注册工具。

@tool
def add(a, b):
return a + b
@tool
def sub(a, b):
return a - b

运行程序。

输出:

注册工具: add
注册工具: sub

再打印:

print(TOOLS)

输出:

{
"add": ,
"sub":
}

到这里,

工具已经完成自动注册。

📖 第五部分:装饰器到底是什么?

很多初学者都会觉得:

@tool

特别神秘。

其实不是。

Python 会自动转换成:

def add(a, b):
return a + b
add = tool(add)

也就是说:

@tool

只是下面这句话的语法糖:

tool(add)

所以:

@tool
def add():
...

等价于:

def add():
...
add = tool(add)

以后看到任何 Python 装饰器,都可以先脑补成这种写法。

💻 第六部分:调用 Tool

现在工具已经注册完成。

增加下面代码:

tool_name = input("请输入工具名称:")
a = int(input("请输入 a:"))
b = int(input("请输入 b:"))
result = TOOLS[tool_name](a, b)
print(result)

运行:

请输入工具名称:
add
请输入 a:
10
请输入 b:
20

输出:

30

如果输入:

sub

输出:

-10

是不是已经开始有 MCP Tool 的感觉了?

📖 第七部分:Tool 不仅保存函数

真正的 MCP Server 保存的不只是函数。

还会保存:

  • Tool 名称
  • Tool 描述
  • Tool 参数
  • Tool Schema
  • 返回值 Schema

所以修改一下 Registry。

TOOLS = {}
def tool(func):
TOOLS[func.name] = {
"func": func,
"description": func.doc
}
return func

然后:

@tool
def add(a, b):
"""两个数字相加"""
return a + b

打印:

print(TOOLS)

输出:

{
"add": {
"func": ,
"description": "两个数字相加"
}
}

是不是已经开始有数据库的感觉了?

📖 第八部分:Python 如何知道参数?

这是今天最重要的一步。

Python 自带一个库:

import inspect

例如:

import inspect
def add(a: int, b: int):
return a + b
print(inspect.signature(add))

输出:

(a: int, b: int)

继续:

sig = inspect.signature(add)
for name, param in sig.parameters.items():
print(name)
print(param.annotation)

输出:

a
<class 'int'>
b
<class 'int'>

Python 已经知道了:

  • 参数名称
  • 参数类型

所以:

def add(a: int, b: int):

这些信息不是给编辑器看的。

运行时 Python 也能读取。

官方 MCP SDK 正是利用这一能力,在注册工具时自动读取函数签名、类型注解和文档字符串,再生成工具元数据和输入 Schema。

💻 第九部分:最终版 Registry

下面是目前最完整的版本。

import inspect
TOOLS = {}
def tool(func):
sig = inspect.signature(func)
params = {}
for name, param in sig.parameters.items():
params[name] = param.annotation
TOOLS[func.name] = {
"func": func,
"description": func.doc,
"parameters": params
}
return func
@tool
def weather(city: str, days: int = 1):
"""查询天气"""
return f"{city} 天气晴"

打印:

print(TOOLS)

输出类似:

{
"weather": {
"func": ,
"description": "查询天气",
"parameters": {
"city": str,
"days": int
}
}
}

🧪 本课练习

请自己完成下面三个工具。

@tool
def add(a: int, b: int):
"""两个数字相加"""
pass
@tool
def multiply(a: int, b: int):
"""两个数字相乘"""
pass
@tool
def hello(name: str):
"""打招呼"""
pass

要求:

运行:

print(TOOLS)

输出每个工具:

  • 名称
  • 描述
  • 参数
  • 函数对象

🎯 本课总结

今天我们已经实现了一个最小版 Tool Registry。

目前已经具备:

✅ Tool 注册

✅ Tool 自动发现

✅ Tool 描述

✅ Tool 参数读取

虽然我们还没有写任何 MCP 协议代码,但已经完成了 MCP Server 中非常重要的一部分:工具注册与元数据收集。

➡️ 第三课预告

下一课我们继续升级。

我们会实现:

让一个 JSON 自动调用 Tool。

例如:

输入:

{
"tool": "add",
"args": {
"a": 10,
"b": 20
}
}

程序自动执行:

add(10, 20)

并返回:

{
"result": 30
}

到第三课结束时,你会发现:一个最小可运行的 Mini MCP Server 已经初具雏形。

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