第三课

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 最重要的能力之一。

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