[TOC]

介绍

帮助小白快速认识、开发和使用MCP。其实我也是小白哦,若有问题或错误欢迎指出,感谢

1. 什么是MCP

MCP(Model Context Protocol)即模型上下文协议,是由 Anthropic 公司于 2024 年底开源发布, 用于标准化应用程序如何向大型语言模型(LLMs)提供上下文。将 MCP 想象成 AI 应用的 USB-C 接口。就像 USB-C 提供了一种标准化的方式来连接设备到各种外设和配件一样,MCP 提供了一种标准化的方式来连接 AI 模型到不同的数据源和工具。

20250521-001.png

2. 为什么用MCP

MCP 帮助你在 LLMs 上构建代理和复杂的工作流。LLMs 经常需要与数据和工具集成,而 MCP 提供了:

  • 一系列不断增长的预构建集成,你的 LLM 可以直接插入
  • MCP 对各个服务的接口进行了统一,这样 M 个 Agent 可以直接使用这 N 个服务,大幅降低重复开发和适配的成本
  • 确保你的数据在你的基础设施内最佳实践
  • 在 LLM 提供者和供应商之间切换的灵活性

20250521-002.png

3. MCP、智能体、大模型 关系

20250521-003.png

4. MCP 架构与组件

  • 主机(Host):与大模型直接交互,处理大模型输入输出,保障数据准确完整,执行安全策略,处理延迟可降低至平均 50 毫秒以内。通常是 AI 应用(Agent),比如 Anthropic Claude Desktop、Cursor、Cline 等,负责选择并调用 MCP Client,以便使用各种 MCP Server 提供的能力。
  • 客户端(Client):位于外部系统,将外部需求转为 MCP 协议格式请求,再把主机响应转换为外部系统能理解的格式 ,请求准备时间可缩短至平均 30 毫秒以内,支持多种编程语言和平台。
  • 服务器(Server):处理主机请求并提供服务,可提供数据查询、文件操作等服务 ,处理能力可达每秒处理 1000 个请求以上,具备灵活扩展机制。提供资源、工具或 Prompts 的服务,如文件系统、数据库、API 等。

20250521-004.png

Client 和 Server 之间使用双向的 JSON-RPC 2.0 进行通信。当前支持 stdio 和 Streamable HTTP 两种传输机制。

5. MCP关键概念

Resources 资源:将服务器中的数据和内容公开给 LLM,

它允许服务器公开可由客户端读取并用作 LLM 交互上下文的数据和内容。

Tools 工具:使 LLM 能够通过您的服务器执行操作

它使服务器能够向客户端公开可执行功能。通过工具,LLM 可以与外部系统交互、执行计算并在现实世界中采取行动。(执行者,API)

Prompts 提示: 创建可重用的提示模板和工作流

使服务器能够定义可重用的提示模板和工作流,客户端可以轻松地向用户和 LLM 显示这些模板和工作流。它们提供了一种强大的方法来标准化和共享常见的 LLM 交互。

Sampling 采样:让您的服务器从 LLM 请求完成

它允许服务器通过客户端请求 LLM 完成,从而在维护安全性和隐私性的同时实现复杂的代理行为。

MCP Server 可以发起请求到 MCP Client,再MCP Client 接收到请求后,执行相应的动作,比如:人工确认或者是调用大模型等。

20250521-005.png

Roots 根:用于定义服务器可以运行的边界。它们为客户端提供了一种将相关资源及其位置通知服务器的方法。

根是客户端建议服务器应该关注的 URI。当客户端连接到服务器时,它会声明服务器应该使用哪些根。虽然 root 主要用于文件系统路径,但 root 可以是任何有效的 URI,包括 HTTP URL。

  • Project directories 项目目录
  • Repository locations 存储库位置
  • API endpoints API 终端节点
  • Configuration locations 配置位置
  • Resource boundaries 资源边界

Transports 运输:模型上下文协议 (MCP) 中的传输为客户端和服务器之间的通信提供了基础。传输处理消息发送和接收方式的基本机制。

MCP 使用 JSON-RPC 2.0 作为其传输格式。传输层负责将 MCP 协议消息转换为 JSON-RPC 格式进行传输,并将收到的 JSON-RPC 消息转换回 MCP 协议消息。

6. 通信方式(Transport)

通信方式介绍场景
STDIO标准输入/输出- 本地集成和命令行工具特别有用;- 构建命令行工具;- 本地集成;- 简单的通信;- 使用 shell 脚本
Server-Sent-Events服务端推流,单向- 需要服务端到客户端的流式传输;- 网络受限
Streamable HTTP流式 Http,将替换 SSE。原因高效灵活,支持更大模块的分布式部署

实战

1. 构建MCP

1.1 前置准备

  • 开发工具:

    • vscode
  • 开发环境:

    • py 3.13
    • nodejs 18
  • 调试工具:

    • modelcontextprotocol/inspector@0.6.0 (依赖node18+)
  • 主机

    • Cherry studio
    • Vscode

1.2 环境搭建

👉 推荐使用uv https://docs.astral.sh/uv/getting-started/installation/

1.2.1 初始化工程
# 初始化框架
uv init mcp-math-demo
# 切换目录
cd mcp-math-demo
# 创建目录(为什么 —> 因为据说是python工程规范)
mkdir -p src/example
# 将生成的main移动到example
mv main.py src/example
1.2.2 创建py虚拟环境
# 创建虚拟环境(工程中会多一个.venv 文件),并激活环境
uv venv
# 激活环境,mac和window 有点差异,需注意下
source .venv/bin/activate
1.2.3 配置pyproject.toml
....其他已经存在的配置....
dependencies = [
    "fastmcp>=2.3.4",
    "mcp[cli]>=1.6.0"
]
[project.scripts]
example = "example.main:main"
[[tool.uv.index]]
name = "mypypi"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
publish-url = "http://your.pypi/"
default = true
....其他已经存在的配置....
1.2.4 安装依赖
uv pip install -e .
1.2.5 运行代码
# 执行工程代码
uv run example
# 若成功则打印结果:Hello from mcp-math-demo! 

1.3 快速编码

一下两种方式任选一种,其中Sse方式需要提前启动

1.3.1 Stdio方式

启动命令测试:uv run src/example/server-math-stdio.py

# server-math-stdio.py
from mcp.server.fastmcp import FastMCP
import logging
 
# 配置日志记录器
logging.basicConfig(
    level=logging.INFO,  # 设置日志级别为 INFO
    format="%(asctime)s - %(levelname)s - %(message)s"  # 日志格式
)
logger = logging.getLogger(__name__)
 
# 创建 FastMCP 实例
mcp = FastMCP("Math")
 
@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers"""
    logger.info("The add method is called: a=%d, b=%d", a, b)  # 记录加法调用日志
    return a + b
 
@mcp.tool()
def multiply(a: int, b: int) -> int:
    """Multiply two numbers"""
    logger.info("The multiply method is called: a=%d, b=%d", a, b)  # 记录乘法调用日志
    return a * b
 
if __name__ == "__main__":
    logger.info("Start math server through MCP-STDIO")  # 记录服务启动日志
    mcp.run(transport="stdio")  # 启动服务并使用标准输入输出通信
1.3.2 Sse方式

👉 该方式需要先启动服务,然后在使用HOST取连接

启动命令测试:uv run src/example/server-math-stdio.py

# server-math-sse.py
from mcp.server.fastmcp import FastMCP
import logging
 
# 配置日志记录器
logging.basicConfig(
    level=logging.INFO,  # 设置日志级别为 INFO
    format="%(asctime)s - %(levelname)s - %(message)s"  # 日志格式
)
logger = logging.getLogger(__name__)
 
# 创建 FastMCP 实例
mcp = FastMCP("Math")
 
@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers"""
    logger.info("The add method is called: a=%d, b=%d", a, b)  # 记录加法调用日志
    return a + b
 
@mcp.tool()
def multiply(a: int, b: int) -> int:
    """Multiply two numbers"""
    logger.info("The multiply method is called: a=%d, b=%d", a, b)  # 记录乘法调用日志
    return a * b
 
if __name__ == "__main__":
    logger.info("Start math server through MCP-SSE")  # 记录服务启动日志
    mcp.run(transport="sse")  # 启动服务并使用标准输入输出通信
    #mcp.run(transport="streamable-http")  # 启动服务并使用标准输入输出通信

1.4 调试MCP

方式一:单独运行调试工具
  • 按照插件(需要按照node18+,建议使用nvm)
# 打开命令行执行脚本
npx @modelcontextprotocol/inspector@0.6.0

Sse

20250521-006.png

Stdio

20250521-007.png

方式二:通过mcp进行调试(实则方式一)
## 这种本质上就是方式一
uv run mcp dev server-math-stdio.py

2. 使用MCP

非开发、开发人员视角演示使用,下面是常用标准的MCP配置

{
    ## 数学计算 方式一:stdio --> server-math-stdio.py
    "math-stdio": {
        # 这个也可以是 .venv/bin/python3
        "command": "uv", 
        # Replace with absolute path to your server-math-stdio.py file
        "args": ["run","xxxx/src/example/server-math-stdio.py"],
        "transport": "stdio",
    },
    ## 数学计算 方式二:sse  --> server-math-sse.py
    "math-sse": {
        "url":"http://127.0.0.1:8000/sse",
        "transport": "sse"
    },
}

2.1 Cherry studio

2.1.1 配置模型

这里可以点击硅基流动进行注册获取(新用户有免费额度),或者根据自己实际情况配置

20250521-008.png

2.1.2 配置MCP服务
2.1.2.1 方式一:stdio

20250521-009.png

--directory
xxx/mcp-math-demo/src/example/
run
server-math-stdio.py
2.1.2.2 方式二:sse

先启动服务:

20250521-010.png

在配置

20250521-011.png

2.1.2.3 查看MCP具体工具

20250521-012.png

2.1.3 选择MCP服务和大模型

20250521-013.png

2.1.4 对话使用MCP

20250521-014.png

2.2 Vscode

2.2.1 下载插件

我用的Roo Code,大家可以下载其他插件: Cline ...

20250521-015.png

2.2.2 配置模型

我这边注册的DeepSeek

20250521-022.png

2.2.3 配置mcp

20250521-016.png

注意:

comand:指定可以运行脚本的命令,环境不要混了

args:参数指定绝对路径

transport: 依据实际, stdio|sse

2.2.4 对话 (过程中自动批准MCP)

输入3*3

20250521-017.png

2.3 Code 代码调用

2.3.1 需要安装依赖
uv add langchain_mcp_adapters langgraph langchain_deepseek
2.3.2 客户端代码 client-langchain.py
import asyncio
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent
from example.utils import init_model
 
## 模型
llm = init_model()
async def main(): 
    client = MultiServerMCPClient(
        {
            # # 数学计算 sse  --> server-math-sse.py
            "math-sse": {
                "url":"http://127.0.0.1:8000/sse",
                "transport": "sse"
            },
            # 数学计算 stdio --> server-math-stdio.py
            # "math-stdio": {
            #     "command": "python",
            #     "args": ["src/example/server-math-stdio.py"],
            #     "transport": "stdio",
            # }
        }
    )
    tools = await client.get_tools()
    agent = create_react_agent(
        llm,
        tools,
        debug=True
    ) 
    # 循环接收用户输入
    while True:
        try:
            # 提示用户输入问题
            user_input = input("\n请输入您的问题(或输入 'exit' 退出):")
            if user_input.lower() == "exit":
                print("感谢使用!再见!")
                break

            # 调用代理处理问题
            agent_response = await agent.ainvoke({"messages": [{"role": "user", "content": user_input}]})

            print("\n>>>>>>>>>>>>>>>>>>>>>>> 开始输出结果 >>>>>>>>>>>>>>>>>>>>>>>\n")
            # 调用抽取的方法处理输出结果
            print_optimized_result(agent_response)
            print("\n<<<<<<<<<<<<<<<<<<<<<<< 结果输出完成 <<<<<<<<<<<<<<<<<<<<<<<\n")

        except Exception as e:
            print(f"发生错误:{e}")
            continue

# 解析并输出结果
def print_optimized_result(agent_response):
    """
    解析代理响应并输出优化后的结果。
    :param agent_response: 代理返回的完整响应
    """
    messages = agent_response.get("messages", [])
    steps = []  # 用于记录计算步骤
    final_answer = None  # 最终答案
 
    for message in messages:
        if hasattr(message, "additional_kwargs") and "tool_calls" in message.additional_kwargs:
            # 提取工具调用信息
            tool_calls = message.additional_kwargs["tool_calls"]
            for tool_call in tool_calls:
                tool_name = tool_call["function"]["name"]
                tool_args = tool_call["function"]["arguments"]
                steps.append(f"调用工具: {tool_name}({tool_args})")
        elif message.type == "tool":
            # 提取工具执行结果
            tool_name = message.name
            tool_result = message.content
            steps.append(f"{tool_name} 的结果是: {tool_result}")
        elif message.type == "ai":
            # 提取最终答案
            final_answer = message.content
 
    # 打印优化后的结果
    print("\n***执行过程***:")
    for step in steps:
        print(f"- {step}")
    if final_answer:
        print(f"\n***最终结果***\n> {final_answer}\n")
 

if __name__ == "__main__":
    asyncio.run(main())
2.3.3 模型工具类utils.py
from langchain_deepseek import ChatDeepSeek
#from langchain.chat_models import init_chat_model
#from langchain_core.language_models import BaseChatModel

def init_model():
    ## 公网-通义模型
    # from langchain_community.chat_models.tongyi import ChatTongyi #dashscope 
    # llm = ChatTongyi( 
    #     temperature=0,
    #     api_key="sk-XXXX",    
    #     )
    # 公网-Deepseek模型
    llm = ChatDeepSeek(
        model="deepseek-chat",  # 尝试不同的模型名称
        api_key="sk-xxxx"
    ) 
    ## 不行坑比较多,可能是版本功能问题,初始化后的llm 总是报各种错误
    # llm = load_chat_model(f"{model_provider}/{model_name}",{
    #     "api_key": api_key,
    #     "base_url": base_url,
    #     # enable_auto_tool_choice: True,
    #     # tool_call_parser:True
    # })
    return llm;
2.3.4 调试过程
  • 启动MCP服务端

20250521-018.png

  • 启动MCP客户端并输入问题:3*4

20250521-019.png

调用流程分析:

20250521-020.png

3. 常见问题

  • python虚拟环境

    • 环境混乱导致包不兼容,命令行中的环境生效
    • 调试工具中的uv环境和工程实际环境不对应
    • # 推荐(局部)
      uv --directory xxx/mcp-math-demo/src/example run server-math-stdio.py
      # 不推荐(依赖全局)
      uv run xxx/mcp-math-demo/src/example/server-math-stdio.py
  • 模型支持

    • mcp 在对话中未调用工具(换模型)基于langchain_mcp_adapters 代码实现来,

      model="deepseek-chat" # 成功

      model="qwen-turbo" # 成功

    • 自己搭建的模型不行

      可以检查一下模型是否支持工具,是否开启工具
  • 权限问题

    • window中通过uv执行代码虚拟环境共用出错

      可以单独创建,就是不要同时在一个虚拟环境中启动两个MCP

4. 相关文档

  • MCP
官网:https://modelcontextprotocol.io/introduction
  • Uv/Uvx:一个帮你高效管理python虚拟环境的工具
官网:https://docs.astral.sh/uv/
  • SpringAI:提供AI编程框架和解决方案,完美整合Spring体系(java17+)
  • npx:让你可以无需安装就能运行npm包里的命令,方便快捷地使用各种Node.js工具和库。(node18+)

这里用于启动调试工具 npx @modelcontextprotocol/inspector@0.6.0

  • Nvm: nvm(Node Version Manager)是一个用于管理Node.js版本的工具,可以方便地在不同项目之间切换Node.js版本,确保开发环境的一致性

5. 总结

  • 简单认识MCP

    • MCP 解决了大模型与外部工具和数据源集成的标准化问题,类似于硬件领域的 USB-C 标准
    • MCP 架构主要包含三个核心组件:主机(Host)、客户端(Client)和服务器(Server),它们通过 JSON-RPC 协议进行通信
    • MCP 提供了四种关键能力:资源(Resources)、工具(Tools)、提示(Prompts)和采样(Sampling),使大模型能够更高效地与外部系统交互
    • MCP 支持多种通信方式,包括标准输入/输出(STDIO)、服务端推流(SSE)和流式 HTTP,适用于不同的使用场景
  • MCP快速入门实战

    • 我们通过构建简单的数学计算 MCP 服务,展示了如何快速开发和部署 MCP 服务,并通过多种方式(Cherry studio、VSCode、自定义代码)调用这些服务。这为开发者提供了清晰的入门指导,帮助理解 MCP 的实际应用流程。

6. 附录


Wen006
0 声望0 粉丝