[TOC]
介绍
帮助小白快速认识、开发和使用MCP。其实我也是小白哦,若有问题或错误欢迎指出,感谢
1. 什么是MCP
MCP(Model Context Protocol)即模型上下文协议,是由 Anthropic 公司于 2024 年底开源发布, 用于标准化应用程序如何向大型语言模型(LLMs)提供上下文。将 MCP 想象成 AI 应用的 USB-C 接口。就像 USB-C 提供了一种标准化的方式来连接设备到各种外设和配件一样,MCP 提供了一种标准化的方式来连接 AI 模型到不同的数据源和工具。
2. 为什么用MCP
MCP 帮助你在 LLMs 上构建代理和复杂的工作流。LLMs 经常需要与数据和工具集成,而 MCP 提供了:
- 一系列不断增长的预构建集成,你的 LLM 可以直接插入MCP 对各个服务的接口进行了统一,这样 M 个 Agent 可以直接使用这 N 个服务,大幅降低重复开发和适配的成本确保你的数据在你的基础设施内最佳实践在 LLM 提供者和供应商之间切换的灵活性
3. MCP、智能体、大模型 关系
4. MCP 架构与组件
- 主机(Host):与大模型直接交互,处理大模型输入输出,保障数据准确完整,执行安全策略,处理延迟可降低至平均 50 毫秒以内。通常是 AI 应用(Agent),比如 Anthropic Claude Desktop、Cursor、Cline 等,负责选择并调用 MCP Client,以便使用各种 MCP Server 提供的能力。客户端(Client):位于外部系统,将外部需求转为 MCP 协议格式请求,再把主机响应转换为外部系统能理解的格式 ,请求准备时间可缩短至平均 30 毫秒以内,支持多种编程语言和平台。服务器(Server):处理主机请求并提供服务,可提供数据查询、文件操作等服务 ,处理能力可达每秒处理 1000 个请求以上,具备灵活扩展机制。提供资源、工具或 Prompts 的服务,如文件系统、数据库、API 等。
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 接收到请求后,执行相应的动作,比如:人工确认或者是调用大模型等。
**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.13nodejs 18
- modelcontextprotocol/inspector@0.6.0 (依赖node18+)
- Cherry studioVscode
1.2 环境搭建
👉 推荐使用uv docs.astral.sh/uv/getting-…
1.2.1 初始化工程
# 初始化框架uv init mcp-math-demo# 切换目录cd mcp-math-demo# 创建目录(为什么 —> 因为据说是python工程规范)mkdir -p src/example# 将生成的main移动到examplemv 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.pyfrom mcp.server.fastmcp import FastMCPimport 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.pyfrom mcp.server.fastmcp import FastMCPimport 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
Stdio
方式二:通过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 配置模型
这里可以点击硅基流动进行注册获取(新用户有免费额度),或者根据自己实际情况配置
2.1.2 配置MCP服务
2.1.2.1 方式一:stdio
--directoryxxx/mcp-math-demo/src/example/runserver-math-stdio.py
2.1.2.2 方式二:sse
先启动服务:
在配置
2.1.2.3 查看MCP具体工具
2.1.3 选择MCP服务和大模型
2.1.4 对话使用MCP
2.2 Vscode
2.2.1 下载插件
我用的Roo Code,大家可以下载其他插件: Cline ...
2.2.2 配置模型
我这边注册的DeepSeek
2.2.3 配置mcp
注意:
comand:指定可以运行脚本的命令,环境不要混了
args:参数指定绝对路径
transport: 依据实际, stdio|sse
2.2.4 对话 (过程中自动批准MCP)
输入3*3
2.3 Code 代码调用
2.3.1 需要安装依赖
uv add langchain_mcp_adapters langgraph langchain_deepseek
2.3.2 客户端代码 client-langchain.py
import asynciofrom langchain_mcp_adapters.client import MultiServerMCPClientfrom langgraph.prebuilt import create_react_agentfrom 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 BaseChatModeldef 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服务端
- 启动MCP客户端并输入问题:3*4
调用流程分析:
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
- Uv/Uvx:一个帮你高效管理python虚拟环境的工具
- SpringAI:提供AI编程框架和解决方案,完美整合Spring体系(java17+)
- npx:让你可以无需安装就能运行npm包里的命令,方便快捷地使用各种Node.js工具和库。(node18+)
这里用于启动调试工具
npx @modelcontextprotocol/inspector@0.6.0
- Nvm: nvm(Node Version Manager)是一个用于管理Node.js版本的工具,可以方便地在不同项目之间切换Node.js版本,确保开发环境的一致性
unix/linux: github.com/nvm-sh/nvmWindow: github.com/coreybutler… (记得申请管理员权限,不然安装不了)
5. 总结
- 简单认识MCP
- MCP 解决了大模型与外部工具和数据源集成的标准化问题,类似于硬件领域的 USB-C 标准MCP 架构主要包含三个核心组件:主机(Host)、客户端(Client)和服务器(Server),它们通过 JSON-RPC 协议进行通信MCP 提供了四种关键能力:资源(Resources)、工具(Tools)、提示(Prompts)和采样(Sampling),使大模型能够更高效地与外部系统交互MCP 支持多种通信方式,包括标准输入/输出(STDIO)、服务端推流(SSE)和流式 HTTP,适用于不同的使用场景
- 我们通过构建简单的数学计算 MCP 服务,展示了如何快速开发和部署 MCP 服务,并通过多种方式(Cherry studio、VSCode、自定义代码)调用这些服务。这为开发者提供了清晰的入门指导,帮助理解 MCP 的实际应用流程。