理解 MCP——从 Function Calling 到 MCP 协议

文章目录

    • [一、起点:LLM 怎么查数据库?](#一、起点:LLM 怎么查数据库?)
      • [Function Calling 的流程](#Function Calling 的流程)
    • [二、Function Calling 的实现:chat.py](#二、Function Calling 的实现:chat.py)
    • [三、问题来了:Function Calling 不能复用](#三、问题来了:Function Calling 不能复用)
    • [四、MCP 是什么?](#四、MCP 是什么?)
      • [MCP 协议本身和 LLM 无关](#MCP 协议本身和 LLM 无关)
      • [MCP 不只有 Tools](#MCP 不只有 Tools)
    • [五、MCP 协议长什么样?](#五、MCP 协议长什么样?)
    • [六、MCP 的架构:把 LLM 和工具执行拆开](#六、MCP 的架构:把 LLM 和工具执行拆开)
    • [六、MCP Server 的实现:mcp_server.py](#六、MCP Server 的实现:mcp_server.py)
    • [七、MCP Client 的实现:mcp_client.py](#七、MCP Client 的实现:mcp_client.py)
    • [八、Client 和 Server 怎么通信?](#八、Client 和 Server 怎么通信?)
      • [stdio 模式(本地,本项目使用的方式)](#stdio 模式(本地,本项目使用的方式))
      • [HTTP 模式(远程)](#HTTP 模式(远程))
      • 两种模式对比
    • [九、chat.py vs mcp_server.py vs mcp_client.py 对比](#九、chat.py vs mcp_server.py vs mcp_client.py 对比)
    • [十、为什么 MCP 是亮点?](#十、为什么 MCP 是亮点?)

本文档记录了从零理解 Function Calling,再到理解 MCP 协议的完整思路。

读完你将能回答:MCP 是什么、为什么需要它、和 Function Calling 有什么区别、Client 和 Server 怎么通信。


一、起点:LLM 怎么查数据库?

LLM 本身只是一个文字接龙引擎,不能直接访问数据库。

要让它能查数据库,需要 Function Calling(工具调用) 机制。

Function Calling 的流程

复制代码
用户:张伟老师的工号是多少?

1. 把用户问题 + 工具列表 发给 LLM
2. LLM 分析:需要查数据库,决定调用 get_teacher_info 工具
3. LLM 返回:{"tool": "get_teacher_info", "args": {"name": "张伟"}}
4. 你的代码执行工具,查 MySQL,得到结果
5. 把结果发回给 LLM
6. LLM 生成自然语言回答:"张伟老师的工号是 T20240001..."

关键点:LLM 自己不执行代码,它只负责"决策"------决定要不要调用工具、调用哪个、传什么参数。真正执行 SQL 的是你写的代码。


二、Function Calling 的实现:chat.py

chat.py 是最直接的实现方式,所有逻辑都在一个文件里:

第一步:手动定义工具列表

告诉 LLM "你有哪些工具可以用":

python 复制代码
TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "get_teacher_info",
            "description": "根据教师姓名查询其工号、所在院系、职称、邮箱",
            "parameters": {
                "type": "object",
                "properties": {
                    "name": {"type": "string", "description": "教师姓名,如:张伟"}
                },
                "required": ["name"],
            },
        },
    },
    # ... 其他工具
]

第二步:本地执行工具

LLM 决定调用工具后,代码本地直接执行:

python 复制代码
def execute_tool(name: str, args: dict) -> str:
    if name == "get_teacher_info":
        rows = query("SELECT ... FROM teachers WHERE name = %s", (args["name"],))
        result = rows[0] if rows else {"error": "未找到"}
    # ...
    return json.dumps(result, ensure_ascii=False)

第三步:对话循环

python 复制代码
def chat_once(messages):
    while True:
        response = client.chat.completions.create(
            model=MODEL, messages=messages, tools=TOOLS, tool_choice="auto"
        )
        msg = response.choices[0].message

        if not msg.tool_calls:
            return msg.content       # 没有工具调用,直接返回答案

        messages.append(msg)
        for tc in msg.tool_calls:
            args = json.loads(tc.function.arguments)
            result = execute_tool(tc.function.name, args)   # ← 本地直接调函数
            messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})
        # 继续循环,带着结果再问 LLM

chat.py 的特点:LLM 调用、工具执行、对话管理全在一个进程里,没有任何"协议",就是普通的函数调用。


三、问题来了:Function Calling 不能复用

假设你用 chat.py 写好了查数据库的工具。

现在你想在 Claude Desktop 里用、在 Cursor 里用、在另一个项目里用------每次都要把工具代码复制过去,还要适配不同的调用方式,没有统一标准。

这就是 MCP 要解决的问题。


四、MCP 是什么?

MCP(Model Context Protocol,模型上下文协议) 是 Anthropic 制定的开放标准:

只要你的工具按照 MCP 协议封装,任何支持 MCP 的 AI 客户端都能直接调用它,不需要修改任何代码。

一句话理解:

MCP 本质也是 Function Calling,只不过通过一个统一的协议,让不同的 AI 只要符合 MCP 协议,就能访问 MCP Server 获得服务。

类比:

  • Function Calling = 每家电器厂商自己造插头,只能插自己家的插座
  • MCP = 统一的 USB 标准,任何设备都能用

MCP 协议本身和 LLM 无关

这是一个重要的细节:MCP 协议本身完全不涉及 LLM,它只定义了 Client 和 Server 之间怎么传消息:

复制代码
Client → Server:你有哪些工具?
Server → Client:有这些工具 [get_teacher_info, get_student_info, ...]
Client → Server:帮我调用 get_teacher_info,参数是 {"name": "张伟"}
Server → Client:结果是 {"name": "张伟", "employee_no": "T20240001", ...}

"把工具列表发给 LLM、LLM 决定调用哪个"------这是 Client 应用自己的逻辑 ,不是 MCP 协议规定的。

MCP Server 不知道、也不关心背后用的是哪个 LLM,甚至可以完全不用 LLM,手动调用 MCP 也完全可以。

MCP 不只有 Tools

本项目只用到了 MCP 的 Tools 能力,但 MCP 完整规范包含三种能力:

能力 作用 本项目
Tools 让 AI 调用函数执行操作(查数据库、发邮件等) ✅ 使用
Resources 向 AI 暴露数据或文件(如数据库 schema、文档内容) ❌ 未使用
Prompts 预定义可复用的提示词模板 ❌ 未使用

理解本项目时聚焦 Tools 即可,但知道 MCP 不止于此。


五、MCP 协议长什么样?

MCP 底层基于 JSON-RPC 2.0 ------一种用 JSON 格式传消息的轻量协议。

每条消息就是一段 JSON,通过 stdin/stdout(本地)或 HTTP(远程)传输。

握手:initialize

Client 启动后第一件事是和 Server 握手,确认双方协议版本:

json 复制代码
Client → Server:
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "clientInfo": {"name": "mcp_client", "version": "1.0"}
  }
}

Server → Client:
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "serverInfo": {"name": "SchoolDB", "version": "1.0"},
    "capabilities": {"tools": {}}
  }
}

获取工具列表:tools/list

json 复制代码
Client → Server:
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/list"
}

Server → Client:
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "tools": [
      {
        "name": "get_teacher_info",
        "description": "根据教师姓名查询其工号、所在院系、职称、邮箱",
        "inputSchema": {
          "type": "object",
          "properties": {
            "name": {"type": "string", "description": "教师姓名,如:张伟"}
          },
          "required": ["name"]
        }
      }
    ]
  }
}

调用工具:tools/call

json 复制代码
Client → Server:
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "get_teacher_info",
    "arguments": {"name": "张伟"}
  }
}

Server → Client:
{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"name\": \"张伟\", \"employee_no\": \"T20240001\", \"department\": \"计算机学院\", \"title\": \"教授\"}"
      }
    ]
  }
}

小结

MCP 协议规定的东西其实很简单:

规定了什么 内容
消息格式 JSON-RPC 2.0,每条消息是一段 JSON
方法名 initialize / tools/list / tools/call 等固定方法名
数据结构 工具用 name + description + inputSchema 描述,结果用 content 包裹
没有规定 用什么 LLM、怎么管理对话、业务逻辑是什么

你在 mcp_client.py 里写的 session.list_tools()session.call_tool(),本质上就是 SDK 帮你把上面这些 JSON 消息发出去、把响应解析回来,不需要手动拼 JSON。


六、MCP 的架构:把 LLM 和工具执行拆开

MCP 把原来 chat.py 里一体的逻辑拆成了两个独立的部分:

复制代码
┌─────────────────────────────┐
│       MCP Client            │  ← Cursor、Claude Desktop,或你自己写的 mcp_client.py
│  - 管理对话                  │
│  - 调用 LLM                 │
│  - 通过 MCP 协议请求工具      │
└────────────┬────────────────┘
             │  MCP 协议(stdin/stdout 或 HTTP)
             ▼
┌─────────────────────────────┐
│       MCP Server            │  ← mcp_server.py
│  - 注册工具                  │
│  - 执行工具(查 MySQL)       │
│  - 返回结果                  │
└─────────────────────────────┘

一次完整的问答流程:

复制代码
用户
 ↓ 提问
MCP Client
 ↓ "你有哪些工具?" (list_tools)
MCP Server → 返回工具列表
 ↓ 带工具列表发给 LLM
LLM → 决定调用 get_teacher_info
 ↓ "帮我调用这个工具" (call_tool)
MCP Server → 查 MySQL → 返回结果
 ↓ 把结果发给 LLM
LLM → 生成自然语言回答
 ↓
用户收到回答

六、MCP Server 的实现:mcp_server.py

@mcp.tool() 装饰器注册工具,比手写 TOOLS 字典简洁很多:

python 复制代码
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("SchoolDB")

@mcp.tool()
def get_teacher_info(name: str) -> dict:
    """
    根据教师姓名查询工号、院系、职称、邮箱。
    参数:
        name: 教师姓名,如 "张伟"
    """
    rows = query(
        "SELECT name, employee_no, department, title, email FROM teachers WHERE name = %s",
        (name,),
    )
    return rows[0] if rows else {"error": f"未找到教师:{name}"}

# 启动 Server,等待 Client 连接
mcp.run(transport="stdio")

@mcp.tool() 自动从函数签名和 docstring 生成工具描述,等价于 chat.py 里手写的整个 TOOLS 字典。


七、MCP Client 的实现:mcp_client.py

这里展示 MCP Client 内部是怎么工作的:

python 复制代码
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    # 1. 告诉 SDK 怎么启动 Server 子进程
    server_params = StdioServerParameters(
        command=sys.executable,
        args=["mcp_server.py"],
        env={...},
    )

    # 2. 启动 Server 子进程,建立 stdin/stdout 管道连接
    async with stdio_client(server_params) as (read_stream, write_stream):
        async with ClientSession(read_stream, write_stream) as session:

            # 3. 握手初始化
            await session.initialize()

            # 4. 从 Server 获取工具列表(MCP 协议的 list_tools 请求)
            tools_response = await session.list_tools()
            openai_tools = mcp_tools_to_openai(tools_response.tools)

            # 5. 带工具列表发给 LLM,LLM 决定调用哪个工具
            response = llm.chat.completions.create(
                model=MODEL, messages=messages, tools=openai_tools, tool_choice="auto"
            )

            # 6. 通过 MCP 协议调用 Server 上的工具(核心!)
            mcp_result = await session.call_tool(tool_name, args)

            # 7. 把结果发给 LLM,生成最终回答

MCP Client 的核心只有两行:

python 复制代码
# 从 Server 拿工具列表
tools_response = await session.list_tools()

# 通过 MCP 协议调用 Server 上的工具
mcp_result = await session.call_tool(tool_name, args)

其余对话逻辑和 chat.py 完全一样。


八、Client 和 Server 怎么通信?

stdio 模式(本地,本项目使用的方式)

Client 直接把 Server 作为子进程启动,通过操作系统的管道通信:

复制代码
mcp_client.py(父进程)
    │
    │  启动子进程:python mcp_server.py
    │
    ├─ 向 Server 的 stdin 写入 → {"method": "tools/call", "params": {...}}
    └─ 从 Server 的 stdout 读取 ← {"result": {...}}
  • 不走网络,不需要端口
  • Client 启动时自动把 Server 跑起来,不需要提前手动启动
  • 这就是为什么 Cursor 配置里只填 commandargs,不填 URL

HTTP 模式(远程)

Server 部署在远程服务器上,Client 通过 HTTP 连接:

复制代码
mcp_client.py  ──── HTTP 请求 ────→  mcp_server(云端)
               ←─── HTTP 响应 ────

两种模式对比

stdio(本地) HTTP(远程)
Server 在哪 同一台机器 任意服务器
需要端口 不需要 需要
谁启动 Server Client 自动启动 提前部署好
典型场景 Cursor/Claude Desktop 接本地工具 把工具部署成云服务

九、chat.py vs mcp_server.py vs mcp_client.py 对比

chat.py mcp_server.py mcp_client.py
角色 一体化(Client + 工具执行) MCP Server MCP Client
工具列表从哪来 自己手写 TOOLS 装饰器自动生成 启动时问 Server
怎么执行工具 本地调函数 接收 MCP 请求后执行 通过 MCP 发给 Server
使用 LLM 豆包 不涉及(只执行工具) 豆包
能被复用吗 不能 能(任何 MCP Client 都能用) ---

chat.py 和 mcp_client.py 唯一的本质区别:

python 复制代码
# chat.py:本地直接调函数
result = execute_tool(name, args)

# mcp_client.py:通过 MCP 协议跨进程调用
result = await session.call_tool(name, args)

十、为什么 MCP 是亮点?

  1. 标准化:工具只写一次,Cursor、Claude Desktop、任何支持 MCP 的客户端都能用
  2. 解耦:LLM 和工具执行完全分离,换 LLM 不影响工具,改工具不影响 LLM
  3. 可扩展:新增工具只需在 Server 加一个函数,Client 下次连接自动获取

本项目中,mcp_server.py 封装了查数据库的能力,在 Cursor 里直接提问就能查学校数据库,不需要写任何对话界面------MCP Server 就是你的工具插件,AI 客户端就是你的前端


项目地址:https://github.com/ErizJ/school-mcp-chat

相关推荐
国产化创客2 小时前
OpenClaw Control UI安全上下文访问配置
ai·webui·openclaw
_oP_i2 小时前
openclaw调用skill的机制
ai
huazi992 小时前
AI编程(一):Trae+Git 应用开发
git·ai编程·trae
xixixi777773 小时前
拥抱AI大模型时代:开发者如何利用智能编程工具提升效率
人工智能·python·ai·大模型·aigc·代码
特别关注外国供应商3 小时前
2025 年-2026 年,Claroty 在 Gartner® CPS 保护平台魔力象限™ 中被评为领导者
ai·工控安全·gartner·合规审计·claroty·cps安全·物联网设备安全
脸ル粉嘟嘟3 小时前
一键部署的AI智能体新标杆
ai编程
LucianaiB3 小时前
从基础配置到架构设计:JiuwenClaw 日报生成器开发实践
人工智能·ai·腾讯云·保姆级·opencalw
心疼你的一切3 小时前
【Unity-MCP完全指南:从零开始构建AI游戏开发助手】
人工智能·unity·ai·游戏引擎·aigc·mcp
zhangshuang-peta3 小时前
保障人工智能集成安全:解决生产环境中的MCP安全漏洞
人工智能·ai agent·mcp·peta