MCP 协议从入门到实战:让大模型拥有调用本地工具的能力

MCP 协议从入门到实战:让大模型拥有调用本地工具的能力

本文基于实际项目经验,从零讲解 MCP(Model Context Protocol)协议的完整实现,包含服务端开发、客户端调用、异步通信流程,以及通义千问大模型的实际对接。

为什么需要 MCP?

大语言模型(LLM)本身有一个根本限制:它只能基于训练数据进行文本生成,无法直接访问外部工具、本地文件或实时数据。

比如,你问 ChatGPT"现在几点了?",它只能根据训练数据猜测,因为模型没有获取实时时间的能力。

MCP(Model Context Protocol)就是为了解决这个问题而生的------它是一套标准化的协议,让大模型能够安全、规范地调用外部工具

类比一下:

  • 没有 MCP:大模型是一个"闭门造车"的专家,什么都能聊但什么都做不了
  • 有了 MCP:大模型长出了"双手",可以操作计算器、查天气、读文件、调用 API

一、MCP 是什么?

MCP 是 Anthropic 提出的开放协议,定义了大模型与外部工具之间的通信标准。核心概念:

  • MCP Server:提供工具的服务端,注册各种可调用功能
  • MCP Client:与大模型对接的客户端,负责将工具暴露给模型
  • stdio 通信:默认通过标准输入输出进行双向通信
arduino 复制代码
┌─────────────┐     stdio      ┌──────────────┐
│  LLM (模型)  │ ←────────────→ │ MCP Client   │
└─────────────┘                └──────┬───────┘
                                      │ stdio
                              ┌───────┴───────┐
                              │  MCP Server    │
                              │  ┌──────────┐  │
                              │  │ Tool 1   │  │
                              │  │ Tool 2   │  │
                              │  │ Tool 3   │  │
                              │  └──────────┘  │
                              └────────────────┘

二、快速上手:写一个 MCP 服务端

环境准备

bash 复制代码
pip install mcp

第一个 MCP Server

使用 FastMCP(官方推荐的高级 API,类似 FastAPI 的装饰器语法):

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

# 1. 初始化 MCP 服务器
mcp_server = FastMCP("my-first-mcp-server")

# 2. 注册工具:加法计算器
@mcp_server.tool()
async def calculate_add(a: float, b: float) -> str:
    """计算两个数字的加法"""
    result = a + b
    return f"加法计算结果:{a} + {b} = {result}"

# 3. 注册工具:获取当前时间
@mcp_server.tool()
async def get_current_time() -> str:
    """获取当前系统的本地时间"""
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    return f"当前系统时间:{now}"

# 4. 注册工具:天气查询
@mcp_server.tool()
async def query_weather(city: str) -> str:
    """查询指定城市的天气"""
    weather_data = {
        "北京": "晴,25℃",
        "上海": "多云,28℃",
        "广州": "小雨,30℃"
    }
    result = weather_data.get(city, f"未找到{city}的天气数据")
    return f"{city}天气:{result}"

# 5. 启动服务
if __name__ == "__main__":
    print("✅ MCP 服务已启动,等待客户端连接...")
    mcp_server.run()  # 默认启动 stdio 通信

关键点:

  • @mcp_server.tool() 装饰器注册工具
  • 工具函数必须是 async
  • 返回值必须是 str(FastMCP 会自动包装为 TextContent
  • 函数的 docstring 会被大模型用来理解工具用途

三、客户端调用:让大模型自动使用工具

服务端写好了,现在需要一个客户端把工具"绑定"给大模型。

完整客户端代码

python 复制代码
import asyncio
import os
from dotenv import load_dotenv
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage

# 加载环境变量
load_dotenv()
api_key = os.getenv('DASHSCOPE_API_KEY', '')
base_url = os.getenv('DASHSCOPE_API_BASE', '')

# 初始化大模型(以通义千问为例,兼容 OpenAI 接口)
llm = ChatOpenAI(
    base_url=base_url,
    model='qwen3.5-plus',
    api_key=api_key,
)

async def main():
    # 1. 配置 MCP 服务端连接参数
    server_params = StdioServerParameters(
        command="python",
        args=["my_mcp_tools/weather_server.py"]
    )

    # 2. 建立 stdio 连接
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            print("✅ 已成功连接到 MCP Server!")

            # 3. 获取服务端所有可用工具
            tools_response = await session.list_tools()
            print(f"🛠️  发现工具:{[t.name for t in tools_response.tools]}")

            # 4. 将 MCP 工具格式转换为 OpenAI 函数格式
            lc_tools = []
            for t in tools_response.tools:
                lc_tools.append({
                    "type": "function",
                    "function": {
                        "name": t.name,
                        "description": t.description,
                        "parameters": t.inputSchema
                    }
                })

            # 5. 将工具绑定到 LLM
            llm_with_tools = llm.bind_tools(lc_tools)

            # 6. 向大模型提问(让它自己决定用什么工具)
            query = "看下现在的时间,然后帮我查北京天气,再算一下 15.5 + 20.3"
            print(f"🧑 提问: {query}")

            response = llm_with_tools.invoke([HumanMessage(content=query)])

            # 7. 执行工具调用
            if response.tool_calls:
                for tool_call in response.tool_calls:
                    print(f"\n🤖 大模型调用工具: {tool_call['name']}")
                    print(f"📦 参数: {tool_call['args']}")

                    result = await session.call_tool(
                        tool_call['name'],
                        arguments=tool_call['args']
                    )
                    print(f"🎯 结果: {result.content[0].text}")
            else:
                print(f"\n🤖 大模型回答: {response.content}")

if __name__ == "__main__":
    asyncio.run(main())

运行结果示例

css 复制代码
✅ 已成功连接到 MCP Server!
🛠️  发现工具:['calculate_add', 'get_current_time', 'query_weather']
🧑 提问: 看下现在的时间,然后帮我查北京天气,再算一下 15.5 + 20.3

🤖 大模型调用工具: get_current_time
📦 参数: {}
🎯 结果: 当前系统时间:2026-05-13 14:30:00

🤖 大模型调用工具: query_weather
📦 参数: {'city': '北京'}
🎯 结果: 北京天气:晴,25℃

🤖 大模型调用工具: calculate_add
📦 参数: {'a': 15.5, 'b': 20.3}
🎯 结果: 加法计算结果:15.5 + 20.3 = 35.8

大模型自主决定了调用哪几个工具、传什么参数、按什么顺序调用------你只需要注册工具,剩下的交给模型。


四、核心流程解析

整个 MCP 调用链路分为 6 步:

markdown 复制代码
1. 启动 MCP Server → 注册工具
2. Client 建立 stdio 连接
3. Client 拉取工具列表(list_tools)
4. 工具格式转换 → MCP Schema → OpenAI Function Schema
5. 绑定工具到 LLM(bind_tools)
6. 用户提问 → LLM 自主决策 → 调用工具 → 返回结果

关键知识点

概念 说明
stdio 通信 MCP 默认通过标准输入输出通信,类似管道
工具 Schema 每个工具有 name、description、inputSchema 三个字段
格式转换 MCP 工具格式 → OpenAI 函数格式,让大模型能理解
自主决策 大模型自己判断是否需要调用工具、调用哪个、传什么参数
异步编程 所有 MCP 通信都是非阻塞的 async/await

五、实战:PaddleOCR MCP 部署

理论讲完了,来看一个真实场景:把 PaddleOCR 部署为 MCP 工具,让大模型能"看懂"图片。

场景

本地有一台 GPU 服务器,部署了 PaddleOCR-VL-1.5。现在想让本机的大模型应用(如 Cursor、Claude Desktop)能调用 OCR 能力。

服务端核心代码

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

mcp_server = FastMCP("paddleocr-server")

ocr = PaddleOCR(use_angle_cls=True, lang='ch')

@mcp_server.tool()
async def ocr_image(image_path: str) -> str:
    """对图片进行 OCR 识别,返回文字内容"""
    result = ocr.ocr(image_path)
    if result and result[0]:
        texts = [line[1][0] for line in result[0]]
        return "\n".join(texts)
    return "未识别到文字"

@mcp_server.tool()
async def ocr_layout(image_path: str) -> str:
    """分析图片的版面结构"""
    # 返回版面分析结果(标题、段落、表格等区域)
    ...

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

部署要点

维度 说明
显存需求 PaddleOCR-VL-1.5 约需 2-4GB 显存
跨局域网 通过 MCP stdio 隧道转发,或改用 HTTP 传输
systemd 部署 写一个 service 文件,开机自启
Docker 部署 用 NVIDIA Container Toolkit 挂载 GPU

六、MCP 的广阔前景

MCP 的价值远不止"让大模型查个天气":

  • 本地文件操作:读文件、写文件、搜索代码
  • 数据库查询:让大模型直接查询你的业务数据
  • API 调用:把任何 REST API 封装为 MCP 工具
  • 系统命令:让大模型执行 shell 命令(注意安全)
  • 多模态:图像识别、语音转写、视频分析
  • Agent 协作:多个 MCP Server 组成工具生态

核心思路:你只需要写工具,大模型会自动学会使用它们。


总结

MCP 协议让大模型从"只能聊天"变成了"能干活"。本文从零讲解了:

  1. MCP 是什么、为什么需要它
  2. 用 FastMCP 写一个服务端(3 个工具)
  3. 用 LangChain + 通义千问做客户端调用
  4. 完整的 6 步调用链路
  5. PaddleOCR MCP 部署实战

下一步你可以:


本文基于实际项目经验编写,代码均可直接运行。如有疑问,欢迎在评论区交流。

相关推荐
阿里云大数据AI技术2 小时前
PAI部署Hermes Agent全攻略,打造越用越懂你的AI助手
人工智能·agent
DigitalOcean2 小时前
如何在无服务器推理上运行 Hermes Agent?
agent
enough_time3 小时前
【无标题】
ai agent·mcp
老码观察4 小时前
数环通iPaaS知识库选型实践:从技术评估到RAGFlow深度调优
人工智能·agent·知识库
DigitalOcean4 小时前
AI 推理产品省钱指南:如何通过基建优化降低 80% 推理成本?
aigc·agent·vibecoding
求知也求真佳4 小时前
S07---S11 | 系统加固闭环总结:让你的 AI Agent 从 “能跑” 到 “稳跑、安全跑、长期跑”
开发语言·agent
JaydenAI5 小时前
[Deep Agents:LangChain的Agent Harness-12]利用create_deep_agent整合所有的Harness中间件
langchain·agent·deep agents·harness
hyunbar5 小时前
Hermes 能不能完全替换 OpenClaw ?
agent·mcp
闲人编程6 小时前
什么是“工具调用”(Function Calling)?Agent的手和脚
大模型·agent·智能体·工具调用·function·calling