第三课
MCP 从零实现教程(三)
第三课:让 JSON 自动调用 Tool
目标:实现一个最小版 Tool Dispatcher(工具调度器)
📚 本课目标
学完本课后,你将能够理解:
- 为什么 MCP 用 JSON 调用 Tool
- 什么是 Dispatcher(调度器)
- 如何根据 JSON 自动调用 Python 函数
- MCP Tool Call 的核心执行流程
今天仍然不使用任何 MCP SDK。
到本课结束,我们已经拥有一个可以接收 JSON、查找 Tool、执行 Tool、返回结果的 Mini MCP。
📖 第一部分:回顾上一课
上一课我们已经有了:
TOOLS = {
"add": add,
"sub": sub,
"weather": weather
}
我们可以这样调用:
TOOLS["add"](10, 20)
输出:
30
但是这里有一个问题。
调用的人必须知道:
TOOLS["add"](10, 20)
Python 代码。
如果调用方不是 Python 呢?
例如:
- ChatGPT
- Claude
- Cursor
- VS Code
- Java
- Go
- Node.js
怎么办?
大家必须有一种共同语言。
这就是:
JSON。
📖 第二部分:为什么选择 JSON?
假设 AI 想调用:
add(10, 20)
如果发送:
请帮我调用 add
程序无法解析。
但是如果发送:
{
"tool": "add",
"args": {
"a": 10,
"b": 20
}
}
程序就知道:
tool = add
参数:
a=10
b=20
所以:
JSON 只是大家共同约定的数据格式。
💻 第三部分:定义调用请求
创建:
request = {
"tool": "add",
"args": {
"a": 10,
"b": 20
}
}
是不是非常简单?
💻 第四部分:最简单的 Dispatcher
下面开始写:
tool_name = request["tool"]
args = request["args"]
result = TOOLS[tool_name](**args)
print(result)
注意:
这里出现了一个新知识:
**args
例如:
args = {
"a": 10,
"b": 20
}
执行:
add(**args)
Python 自动变成:
add(
a=10,
b=20
)
输出:
30
以后 MCP 所有 Tool 调用,
基本都会看到:
func(**args)
📖 第五部分:封装 Dispatcher
我们不希望每次都写:
tool_name = ...
args = ...
TOOLS[tool_name](...)
所以封装一下:
def dispatch(request):
tool_name = request["tool"]
args = request["args"]
func = TOOLS[tool_name]
return func(**args)
调用:
result = dispatch(request)
print(result)
输出:
30
Dispatcher 已经完成。
💻 第六部分:增加错误处理
如果用户输入:
{
"tool": "abc"
}
怎么办?
程序会报错。
增加判断:
def dispatch(request):
tool_name = request["tool"]
if tool_name not in TOOLS:
raise Exception("Tool 不存在")
args = request["args"]
func = TOOLS[tool_name]
return func(**args)
测试:
request = {
"tool": "abc",
"args": {}
}
输出:
Exception: Tool 不存在
是不是更安全了?
💻 第七部分:返回 JSON
真正的 MCP 不会直接:
return 30
而是返回:
{
"success": true,
"result": 30
}
修改:
def dispatch(request):
tool_name = request["tool"]
if tool_name not in TOOLS:
return {
"success": False,
"error": "Tool 不存在"
}
args = request["args"]
result = TOOLS[tool_name](**args)
return {
"success": True,
"result": result
}
运行:
print(dispatch(request))
输出:
{
"success": True,
"result": 30
}
是不是开始越来越像接口了?
💻 第八部分:完整代码
import inspect
TOOLS = {}
def tool(func):
TOOLS[func.__name__] = func
return func
@tool
def add(a: int, b: int):
return a + b
@tool
def weather(city: str):
return f"{city} 今天晴天"
def dispatch(request):
tool_name = request["tool"]
if tool_name not in TOOLS:
return {
"success": False,
"error": "Tool 不存在"
}
args = request["args"]
result = TOOLS[tool_name](**args)
return {
"success": True,
"result": result
}
request = {
"tool": "weather",
"args": {
"city": "北京"
}
}
print(dispatch(request))
输出:
{
"success": True,
"result": "北京 今天晴天"
}
📖 第九部分:这和 MCP 到底有什么关系?
现在回头看看。
我们收到:
{
"tool": "weather",
"args": {
"city": "北京"
}
}
程序做了:
收到 JSON
│
▼
找到 Tool
│
▼
找到 weather()
│
▼
执行 weather(city="北京")
│
▼
返回结果
这就是 MCP Tool Call 的核心流程。
真正的 MCP 比我们多了很多内容:
- Tool Schema
- 参数校验
- JSON-RPC 消息格式
- 流式返回(Streaming)
- 协议版本
- 能力发现(Capability Discovery)
- 错误码规范
但执行工具的核心思想,与我们写的 Dispatcher 是一致的。官方协议就是在这个基础上增加了统一的消息格式和生命周期。(modelcontextprotocol.io)
🧪 本课练习
请新增两个 Tool。
@tool
def multiply(a: int, b: int):
return a * b
@tool
def hello(name: str):
return f"你好,{name}"
然后分别构造:
request = {
"tool": "multiply",
"args": {
"a": 6,
"b": 8
}
}
以及:
request = {
"tool": "hello",
"args": {
"name": "John"
}
}
确保:
print(dispatch(request))
都能得到正确结果。
🎯 本课总结
今天我们完成了一个 Mini MCP 的第二个核心模块。
目前已经拥有:
✅ Tool 注册
✅ Tool 元数据
✅ JSON 请求
✅ Dispatcher(工具调度)
✅ JSON 返回结果
你已经可以把它理解为:
JSON
│
▼
Dispatcher
│
▼
Tool Registry
│
▼
Python Function
这就是一个最小可运行的 Tool 调用系统。
➡️ 第四课预告
下一课开始,我们会进入真正的 MCP 协议世界。
我们将实现:
Tool Discovery(工具发现)
也就是客户端发送:
{
"method": "tools/list"
}
服务器自动返回:
{
"tools": [
{
"name": "add",
"description": "两个数字相加",
"parameters": {
"a": "int",
"b": "int"
}
}
]
}
到第四课结束,你会第一次体验到:我们的 Mini MCP 已经能够模拟官方 MCP Server 最重要的能力之一。

浙公网安备 33010602011771号