参考目录

概述

想象一下,你有一个非常博学但”与世隔绝”的助理(这就是LLM,比如Claude、DeepSeek、GPT-4)。他知识渊博,但只能告诉你他学过的东西,无法接触你电脑里的文件、你的日历或者互联网上的最新信息。
MCP(模型上下文协议-Model Context Protocol) 给LLM打破这层壁垒,真正实现“万能”的LLM提供可能。

在本篇教程中你将了解:

  • 什么是MCP
  • MCP的几个核心概念
  • MCP的三种通信方式
  • MCP服务端与客户端架构

你将掌握:

  • 使用uv管理python环境
  • 自己编写mcp服务端
  • 使用桌面客户端使用自己的MCP服务
  • 自己编写mcp客户端

MCP如此强大,但是实现起来并不复杂,让我们动手实现MCP服务前,先让我们准备好开发和运行环境。

快速入门

uv管理环境

什么是uv

uv 是一款现代化的 Python 项目与依赖管理工具,比传统的 pip 和 conda 更高效,尤其适合管理虚拟环境和依赖解析。
同时MCP(Model Context Protocol)官方也推荐使用 uv 进行环境管理。

更多 uv 的功能详细介绍,请参阅 uv官方文档 https://docs.astral.sh/uv/

uv安装

如果你是在Windows开发,在powershell中执行下面命令:

1
2
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

如果是在linux开发,在终端执行下面命令:

1
curl -LsSf https://astral.sh/uv/install.sh | sh

验证:
安装完成后在终端使用命令 uv --version 如果出现uv的版本信息,类似 uv 0.8.13 说明安装成功,出现找不到uv命令等错误提示说明未安装成功,尝试重新安装。

设置 UV 镜像源(可选)

为提高依赖下载速度,可以设置国内镜像源。

  1. 方法一:通过环境变量设置

设置系统环境变量:

  • 新增变量名:UV_DEFAULT_INDEX

  • 变量值:https://pypi.tuna.tsinghua.edu.cn/simple

tips:设置后需重启 IDE 才能生效。

  1. 方法二:在命令中显式指定源
    一般用在使用docker 打包镜像时使用,适合临时使用的情况,使用 --default-index 参数,例如:
    uv add --default-index https://pypi.tuna.tsinghua.edu.cn/simple "mcp[cli]"

初始化项目

  1. 从终端进入你的工作目录,例如 D:/VSCode/workspace/

    • 文中提到的终端既可以是Windows的cmd命令行,也可以是编辑器的终端,建议选择VSCode,之后写代码也在这里方便一点。
    • VSCode开启终端的快捷键是 ctrl+shift+` (ctrl+shift+tab上方那个键)
  2. 使用命令 uv init [project-name] , 例如 uv init choose-lunch-mcp ,这样你就创建了一个最简单的项目,项目名称为 choose-lunch-mcp, 这个目录就是项目的根目录。

    • 如果本地之前没有安装过python环境,或python环境没有被添加到环境变量中(即python命令不能在终端直接使用),那么uv会认为你本地没有python环境,自动根据你的设备条件自动安装合适的python版本。

    如果你之前已经建立好了项目文件夹,例如 choose-lunch-mcp ,那么你就可以在命令行进入项目目录,然后使用 uv init 命令来初始化项目的基本结构,他不再自动创建项目目录,而是将当前所在的目录作为项目根目录,效果同上面的 uv init [project-name] 相同。

    进入项目目录会看到 uv init 命令自动为你生成了最简单的python项目结构树:

    1
    2
    3
    4
    5
    6
    mcp-demo/
    ├── .python-version # 使用的 Python 版本
    ├── main.py # 主程序入口
    ├── pyproject.toml # 项目配置与依赖声明
    └── README.md # 项目说明

    其中 pyproject.toml 是项目的核心配置文件,可以自己修改,内容类似于:

    1
    2
    3
    4
    5
    6
    7
    8
    [project]
    name = "choose-lunch-mcp"
    version = "0.1.0"
    description = "A demo MCP server for choosing lunch based on hunger level."
    readme = "README.md"
    requires-python = ">=3.8"
    dependencies = []

编写mcp服务器

安装mcp库

终端运行命令:

1
uv add "mcp[cli]"

编写mcp服务

在main.py文件中写一个简单的mcp服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from mcp.server.fastmcp import FastMCP

# 初始化 MCP 服务器
mcp = FastMCP("lunch-server")

@mcp.tool()
def choose_lunch(hungry_status: str) -> str | None:
"""根据饥饿程度推荐午餐选项

Args:
hungry_status: 饥饿程度,可选值为「非常饿」、「有点饿」、「不饿」

Returns:
推荐餐食及其特点描述,若输入不合法则返回 None
"""
if hungry_status == "非常饿":
return "牛肉饭,特点:分量足,能量高,适合非常饿的时候。"
elif hungry_status == "有点饿":
return "鸡肉三明治,特点:营养均衡,适合小饿。"
elif hungry_status == "不饿":
return "水果沙拉,特点:清淡健康,适合不太饿。"
else:
return None

if __name__ == "__main__":
# 启动 stdio 传输模式的服务器
mcp.run(transport="stdio")

我们采用最常用的stdio方式进行通信。

客户端调用MCP服务

这里我们选择桌面版客户端来调用刚才完成的MCP服务,VSCode、Cursor、Cherry Studio什么的都可以,这里我们用VSCode做演示。
具体步骤

  1. 在VSCode的插件市场里安装 Github Copilot 插件,根据提示启用相关服务,使用 ctrl+alt+i 打开Chat窗口,验证可以在窗口里进行提问并获得回答,说明插件配置完成。

  2. 插件安装完成后,在Chat窗口的左下角模式切换为Agent(Agent模式才支持mcp),模型选择Claude Sonnet3.5或其他支持mcp服务的模型,此时VSCode就是一个支持MCP服务的客户端,进行提问时会自行决定是否调用相关的MCP服务。

  3. 点击Chat窗口的右下角第一个扳手图标,在弹出的Configure Tools窗口右上角有个别针的符号,指针悬停上面可以看到Add MCP Server,点击后会先选通信方式,我们上面编写的mcp服务器中配置的mcp.run(transport="stdio")所以这里选择 stdio

  4. VSCode这时候会自动在当前目录创建一个.vscode文件夹,里面初始化了一个mcp.json的配置文件,在这个文件中按照这种下面的格式配置此MCP名称、自己编写的mcp启动文件路径、启动命令等(第三步操作也可以手动执行第四步来替代),例如:

    mcp客户端配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    {
    "servers": {
    "my-mcp-server-choose_lunch": {
    "type": "stdio",
    "command": "uv",
    "args": [
    "--directory",
    "F:/choose-lunch-mcp",
    "run",
    "main.py"
    ]
    }
    }
    }

    或者你也可以自己手动在项目根目录创建文件夹.vscode,在.vscode目录中创建文件mcp.json,编辑此文件,写入刚才mcp的配置信息,一样的效果。

  5. 验证:

    • 方式1:mcp.json文件中choose-lunch-mcp这一行上面会出现running|stop|restart|1 tools|More 智能提示
    • 方式2:Chat的问答框中点击扳手🔧符号,搜索刚才的server名字,能搜到说明服务正常,同时你也能在刚才启动mcp服务的终端看到外部调用的请求记录。
    • 方式3:保存文件后VSCode会自行尝试连接,检测有哪些工具可用,这时候如果没问题VSCode会提示发现了新工具。
  6. 在Chat问答框中提问与定义的mcp工具功能相关的问题,如果回答问题需要使用此mcp,Agent会自动选择此mcp服务,点击continue就会进行传参获得结果,并将结果重新组织后给你回答。
    例如我这里看到客户端向MCP服务器的请求参数:

    1
    2
    3
    {
    "hungry_status": "不饿"
    }

    MCP服务器的返回结果:

    1
    2
    3
    4

    {
    "result": "水果沙拉,特点:清淡健康,适合不太饿。"
    }

    以及大模型根据MCP服务器的返回结果重新组织语言后的最终回答:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    根据 MCP 推荐:

    推荐:水果沙拉
    理由:清淡健康,容易消化,适合现在不太饿的状态
    搭配建议:
    主料:时令水果(苹果、橙子、草莓等)
    配料:少许坚果(提供蛋白质和健康脂肪)
    酱料:可选蜂蜜或酸奶(提升口感)
    如果想要其他选项或者想要更详细的食材搭配建议,请告诉我。

    关闭mcp服务时关不掉会提示你等待mcp client终止连接,vscode中可以在mcp.json中运行智能提示中的stop就能终止client的连接。

这就是完整的mcp服务器编写,客户端访问的全流程,是不是感觉挺神奇的!

sse传输方式

刚才体验的是stdio传输方式,stdio方式也是个人开发者最常用方式,因为它消耗资源最少,不用在后台长期等待,什么时候需要什么时候调用。当然有时候需要mcp服务器常驻后台,例如mcp服务提供资源的访问,这时就需要sse方式,下面看看这种方式的使用与stdio有什么区别。

  1. 区别1:mcp编写代码不同
    1
    2
    3
    if __name__ == "__main__":
    # 启动 sse 传输模式的服务器
    mcp.run(transport="sse")

区别在于主函数中的mcp启动参数不一样。

  1. 区别2:mcp客户端配置文件不同
    由于sse方式mcp服务器是常驻后台的,因此它不是与工具调用同时运行的,在配置文件中需要配置mcp服务器运行的ip端口,遵循下面的基本结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "servers": {
    "choose-lunch-mcp": {
    "type": "sse",
    "url": "http://localhost:8000/sse"
    }
    }
    }

  2. 区别3:启动方式不同
    stdio方式在mcp服务器编写完成后不需要启动服务,只需要将mcp服务器实现文件的路径配置到mcp.json中即可,当Agent决定使用此工具时,才会启动此mcp服务器进行调用。
    sse方式在mcp客户端调用mcp前需要先自行启动服务。

以上面编写的代码为例,在使用客户端进行提问前需要先手动运行main.py,当看到有类似信息在终端时说明服务启动完成。

1
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

在尝试过mcp流程后,下面我们来详细了解MCP相关概念。

什么是MCP?

MCP(模型上下文协议-Model Context Protocol)是一个开放标准和通信协议,由 Anthropic 公司提出并主导开发。

它的核心目标是:标准化大型语言模型(LLM)与外部工具、数据源和服务之间的交互方式

在 MCP 诞生之前,为 LLM(如 Claude、ChatGPT)开发插件或工具连接(如读取数据库、调用 API)是一个复杂且封闭的过程。
每个模型厂商(OpenAI, Anthropic, Google等)都有自己的一套插件体系,开发者需要为每个平台重复开发功能相似的工具集成,这造成了巨大的碎片化和开发负担。

MCP 旨在解决这一问题,它定义了一套通用的”语言”和规范,它具备下面的三大优势:

  • 模型无关:任何支持 MCP 的 LLM(如 Claude, 未来可能包括其他模型)都可以与任何遵循 MCP 标准的工具或数据源进行交互。

  • 传输层无关:MCP 可以在不同的通信通道上运行,例如 stdio(标准输入输出)、SSE(服务器发送事件) 或 HTTP。

  • 双向通信:它允许服务器(提供工具和数据的一方)和客户端(LLM 应用,如 Claude Desktop)之间进行丰富的双向信息交换。

简单来说,MCP 是 LLM 世界的”USB 协议”。就像 USB 标准允许任何一台电脑(客户端)识别并使用任何一款U盘、键盘或打印机(服务器)一样,MCP 允许任何支持它的 AI 应用使用任何遵循该协议的工具。

它让AI模型拥有了安全、标准化的方式去”使用”外部世界,而不是仅仅”谈论”外部世界。

注意:这里有一个误区,很多人误以为MCP是一个工具。MCP从名称看它是一个协议,遵循这个协议的所有工具都可以与支持MCP协议的LLM进行协作,可以理解为:MCP是LLM与数据源、工具、服务之间进行协作的标准语言

MCP 的能力

MCP 协议主要定义了三种核心能力,MCP服务器(遵循MCP协议的注册中心)可以向客户端(LLM)提供工具、资源、提示词。

  1. 工具(Tools):
  • 定义:服务器可以暴露一系列可执行的函数(或命令)给 LLM。

  • 交互:LLM 可以根据用户请求,决定调用哪个工具,并生成符合工具期望格式的参数。服务器执行该工具后,将结果(成功或错误)返回给 LLM。

  • 示例:执行 Shell 命令、调用服务提供商的API、在数据库中运行查询、控制智能家居设备开启。

  1. 资源(Resources):

定义:服务器可以暴露一系列可读的数据源给 LLM。这些资源是静态的或动态生成的内容,LLM 可以按需读取并将其作为上下文信息。

交互:LLM 在回答问题前,可以”读取”这些资源的内容,就像用户手动上传了一个文件一样。这使得 LLM 能够获取实时、私有的外部信息作为知识库补充。

示例:读取数据库中的特定表、获取服务器上的日志文件等。

  1. 提示模板(Prompts):

定义:服务器可以预定义一些高质量的提示词模板。

交互:客户端(如 AI 应用)可以向用户展示这些模板,用户选择后,模板会被填充并发送给 LLM。这简化了复杂任务的启动过程,确保了提示词的质量和一致性。

示例:”代码审查助手”、”博客大纲生成器”、”SQL 查询解释器”等模板。

MCP 的价值和未来

价值:

  • 对于开发者:一次开发,多处运行。开发者只需为他们的工具或数据源编写一个 MCP 服务器,所有支持 MCP 的 AI 应用和平台都能立即使用它,极大降低了开发成本和维护负担,按照mcp标准开发的功能代码能直接被注册为一个mcp服务。

  • 对于用户:获得了前所未有的自由度和灵活性。用户可以从丰富的生态中选择自己需要的 MCP 服务器(工具),并将其组合到他们喜欢的 AI 应用(客户端)中,打造高度个性化的AI助手。

  • 对于模型厂商:促进了模型能力的边界拓展。模型不再局限于其训练数据截止日期之前的知识,也不再受限于内置的有限功能。通过 MCP,模型可以访问实时信息、执行动作、处理私有数据,从而变得更加强大和实用。

未来:

  • 生态繁荣:预计会出现一个庞大的、由社区和商业公司驱动的 MCP 服务器生态系统,覆盖软件开发、数据分析、办公自动化、物联网等各个领域。

  • 标准化与互操作性:MCP 有望成为AI工具集成领域的事实标准,结束当前的”插件战争”,推动整个行业的互操作性和创新速度。

  • AI 操作系统的雏形:MCP 协议可以被视为未来”AI操作系统”的基础层。LLM 作为”CPU”,而各种 MCP 服务器则相当于”驱动程序”和”系统调用”,管理着对硬件资源和软件服务的访问。

核心概念

  • Client (客户端): 托管 LLM 的应用,如 Claude Desktop、Cursor IDE这种桌面客户端或者自己编写的代码客户端。

  • Server (服务器): 你编写的程序,提供特定的功能,那些工具、资源、提示词的定义、实现都在这里。

  • Resources (资源): 服务器提供的可读数据源(如文件、数据库查询结果)。客户端可以”读取”它们。

  • Tools (工具): 服务器提供的可执行函数。客户端可以”调用”它们来执行操作(如运行命令、写文件、调用 API)。

  • Prompts (提示): 服务器提供的预制提示词模板。客户端可以获取并注入到用户的对话中。

  • Annotations (注解): 附加在 Resources 和 Tools 上的元数据,用于向 LLM 和用户更好地描述其用途和行为。

下面的实现方式都是基于Python SDK进行开发的。

你需要关注下面几个常用的概念,对于上下文Context、图像Images、采样Sampling、会话属性和方法Session Properties and Methods等高级用法可自行查阅提供的链接文档。

服务器-客户端

MCP 采用客户端-服务器(Client-Server)架构,并通过 JSON-RPC 2.0 进行消息传递。

遵循客户端-服务器架构

  • MCP 客户端(如 VS Code)连接到 MCP 服务器并代表 AI 模型请求操作
  • MCP 服务器提供一个或多个工具,通过定义明确的接口公开特定功能
  • 模型上下文协议 (MCP) 定义了客户端和服务器之间通信的消息格式,包括工具发现、调用和响应处理

例如,文件系统 MCP 服务器可以提供用于读取、写入或搜索文件和目录的工具。GitHub 的 MCP 服务器提供用于列出存储库、创建拉取请求或管理问题的工具。MCP 服务器可以在本地机器上运行,也可以远程托管,VSCode 支持这两种配置。

通过标准化这种交互,MCP 消除了每个 AI 模型和每个工具之间进行自定义集成的需要。这使你可以通过简单地向工作区添加新的 MCP 服务器来扩展 AI 助手的功能。

其Client-Server加工如下图所示:

MCP 客户端(Client)

通常是集成 LLM 的应用程序,如 Claude Desktop、Cherry Studio、VSCode插件或未来其他的AI应用。

职责:

  • 初始化与管理一个或多个 MCP 服务器的连接;
  • 将服务器的能力(工具、资源、提示)暴露给 LLM;
  • 将 LLM 的请求转发给服务器;将服务器的响应返回给 LLM。

MCP 服务器(Server)

一个独立的进程,负责管理与外部系统(如数据库、API、文件系统)的实际连接和交互。

职责:

  • 在启动时向客户端宣告其提供的能力;
  • 监听客户端的请求;
  • 执行具体的工具函数或读取资源;
  • 将结果格式化后返回给客户端。

通信流程示例

  1. 握手:客户端启动服务器进程,双方交换初始消息,协商协议版本和能力。
  2. 能力广播:服务器发送 initialize 消息,告知客户端它提供了哪些 tools、resources 和 prompts。
  3. 用户向客户端提出请求(“请总结我最近的代码提交”)。
  4. LLM 决定需要调用 git_log 工具或读取 git_log.txt 资源。
  5. 客户端向服务器发送 callTool 或 readResource 请求。
  6. 服务器执行相应操作(如运行 git log 命令),然后将结果返回给客户端。
  7. 客户端将结果作为上下文插入LLM的对话中,LLM据此生成最终回答给用户。

资源 Resources

资源是向 LLM 公开数据的方式。它们类似于 REST API 中的 GET 端点 - 它们提供数据,但不应执行大量计算或产生副作用。
示例:

@mcp.resource()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from mcp.server.fastmcp import FastMCP


mcp = FastMCP(name="Resource Example")

@mcp.resource("file://documents/{name}")
def read_document(name: str) -> str:
"""Read a document by name."""
# This would normally read from disk
return f"Content of {name}"


@mcp.resource("config://settings")
def get_settings() -> str:
"""Get application settings."""
return """{
"theme": "dark",
"language": "en",
"debug": false
}"""

通过@mcp.resource() 可让LLM通过MCP服务器获取文件内容、读取文件中的配置信息等。

工具 Tools

工具定义最相关的@mcp.tool()注解。

@mcp.tool() 注解用于将一个方法注册为一个mcp工具,它将复杂的参数定义,JSON拼接操作融入到简单的Python方法的定义注释中去,例如:

@mcp.tool()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from mcp.server.fastmcp import FastMCP

mcp = FastMCP(name="Tool Example")


@mcp.tool()
def sum(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b


@mcp.tool()
def get_weather(city: str, unit: str = "celsius") -> str:
"""Get weather for a city."""
# This would normally call a weather API
return f"Weather in {city}: 22degrees{unit[0].upper()}"

其中sum(a: int, b: int) -> int 就说明了工具名称,需要的参数数据类型、数目,返回的结果数据类型。"""Add two numbers together."""说明了工具的用途,相当于规范中的@description注解。
示例中只对工具的功能做了描述,如果你还需要对请求参数或返回参数作解释,也可以和平时使用注释一样,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@mcp.tool()
def get_weather(city: str, unit: str = "celsius") -> str:
"""获取城市天气信息

Args:
city (str): 城市名称
unit (str, optional): 温度单位,默认为摄氏度

Returns:
str: 天气信息
"""
# This would normally call a weather API
return f"Weather in {city}: 22 degrees {unit[0].upper()}"

因此,如果你以往的代码都遵循python的开发规范来定义和写注释,那么恭喜你,只需要给方法前简单地加上@mcp.tool()注解你就能将他注册为一个合格有效地mcp方法了。

这样一个方法称为一个工具,一个mcp服务器中可以包含多个工具。

提示词模板 Prompts

提示词是可重用的模板,可帮助 LLM 有效地与您的服务器交互

Prompt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from mcp.server.fastmcp import FastMCP
from mcp.server.fastmcp.prompts import base

mcp = FastMCP(name="Prompt Example")


@mcp.prompt(title="Code Review")
def review_code(code: str) -> str:
return f"Please review this code:\n\n{code}"


@mcp.prompt(title="Debug Assistant")
def debug_error(error: str) -> list[base.Message]:
return [
base.UserMessage("I'm seeing this error:"),
base.UserMessage(error),
base.AssistantMessage("I'll help debug that. What have you tried so far?"),
]

结构化输出

默认情况下,如果工具的返回类型与工具中对返回类型的注释兼容一致,则工具将返回结构化结果。否则,它们将返回非结构化结果。
没有类型提示的类无法序列化以进行结构化输出。只有具有正确注释属性的类才会被转换为 Pydantic 模型以进行模式生成和验证。
结构化输出支持以下返回类型:

  • Pydantic 模型(BaseModel 子类)
  • TypedDicts 字典
  • 数据类和其他具有类型提示的类
  • dict[str, T] (其中 T 是任何 JSON 可序列化类型)
  • 原始类型(str、int、float、bool、bytes、None)- 包装在 {“result”: value} 中
  • 泛型类型(list, tuple, Union, Optional等)- 包装在 {“result”: value} 中

工具、资源、提示词的联系

  • @mcp.tool():提供的是 “函数能力”。LLM 可以调用它来执行一个操作(比如搜索、计算、调用 API)。

  • @mcp.resource():提供的是 “数据内容”。LLM 可以读取它来获取静态或动态的信息(比如文件内容、数据库记录)。

  • @mcp.prompt():提供的是 “预设指令或模板”。它本质上是一个 可重用的提示词模板,帮助 LLM 更好地、更一致地执行特定类型的任务。

@mcp.prompt() 允许服务器向 LLM 客户端注册一个预定义的提示词模板(核心价值在于 封装和复用复杂的提示逻辑)。

当 LLM 需要使用这个模板时,客户端会向服务器请求该模板的具体内容,然后将其插入到对话上下文中,从而指导 LLM 的行为。

一个具体的例子对比三者,假设我们有一个”天气分析”的 MCP 服务:

使用 @mcp.resource("current_weather)"
LLM 可以读取 weather://beijing 这个 URI,服务器返回"北京,晴,25°C"这样的原始数据。

使用 @mcp.tool("analyze_weather)"
LLM 可以调用这个工具,传入城市名。工具内部可能调用天气 API,然后不仅返回数据,还可能进行一些分析,比如返回 { "city": "beijing", "temp": 25, "analysis": "天气适宜户外活动" }

使用 @mcp.prompt("weather_report_template)"
这个模板本身可能是一段精心设计的文字:
"你是一个专业的天气预报员。请根据以下天气数据,生成一段生动、友好、包含穿衣建议和活动推荐的中文播报。数据:{{weather_data}}"

完整过程:

  1. LLM从resource获取"北京,晴,25°C"原始数据
  2. LLM调用tool得到分析结果{ "city": "beijing", "temp": 25, "analysis": "天气适宜户外活动" }
  3. LLM将分析结果传入prompt,LLM 根据这个高质量的、具体的prompt指令,生成一段专业的播报:“各位观众大家好!今天北京晴空万里,气温舒适,在25摄氏度左右......”

stdio、sse、http区别和联系

MCP 的三种传输方式(stdio、sse、http)决定了客户端(如 LLM)和服务器(你的基于mcp协议的工具)之间如何建立连接和交换数据。

  1. Stdio (标准输入/输出)
    它是如何工作的:客户端(如VSCode)直接启动一个指定的mcp服务器进程。之后,客户端通过子进程的标准输入(stdin) 向服务器发送数据,并通过标准输出(stdout) 读取服务器的响应。就像你在终端里输入命令并看到输出一样。

    特点

    • 紧密耦合:服务器的生命周期完全由客户端管理。客户端启动,服务器开始工作;客户端退出,服务器也被终止。
    • 高性能:由于是在同一台机器上进程间通信,延迟极低。
    • 最安全:通常仅限于本地运行,天然隔离了网络攻击。

    适用场景:

    • 个人生产力工具:例如,一个用来搜索你本地代码库的服务器。
    • 开发和调试:这是最简单、最常见的开发MCP服务器的方式。
    • 随用随启,不需要长期保持运行的工具。
    • 就像你和你的大脑之间的关系:你想举手(调用工具),你的大脑(客户端)直接通过神经(stdio)向你的手(服务器)发送信号,手执行动作后立刻通过神经返回”完成”的信号。整个过程是直接、快速、且封闭在身体内部的。
  2. SSE (服务器发送事件)
    它是如何工作的:服务器作为一个独立的、长期运行的守护进程(Daemon) 先行启动,并在一个特定的网络端口上监听。客户端使用 SSE 这种协议主动连接到这个已经存在的服务器端点。连接建立后,服务器可以随时向客户端推送消息(事件),类似订阅。

    虽然叫”服务器发送事件”,但 MCP over SSE 仍然是双向通信。客户端通过向一个单独的 /messages 端点发送 POST 请求来向服务器传递信息。

    特点

    • 松耦合:服务器和客户端是独立的进程,甚至可以运行在不同的机器上。
    • 支持远程:允许连接到局域网或互联网上的远程服务器。
    • 实时性:SSE 设计用于实时推送数据,非常适合传输实时日志、状态更新等。

    适用场景

    • 团队共享工具:例如,一个连接公司内部任务管理系统(如Jira)的MCP服务器,团队所有成员都可以配置他们的客户端来连接它。
    • 访问远程资源:连接到一个管理数据库的服务器,该服务器运行在安全的内部网络中。
    • 需要实时更新的工具:例如,一个监控系统状态的服务器,可以主动向Agent推送警报。
    • 就像订阅一个电报频道:频道(SSE服务器)一直开着。你(客户端)主动订阅(连接)了这个频道,之后频道主(服务器)就可以随时向你推送新消息(事件)。如果你想和频道主互动(发送请求),你需要另外给他发一封电报(POST请求)。
  3. HTTP (请求-响应)
    它是如何工作的:这类似于你与任何普通网站 API 的交互。服务器作为一个 HTTP 服务运行。当客户端需要调用工具或读取资源时,它向服务器的一个特定 URL 发送一个 HTTP POST 请求,然后等待服务器的 HTTP 响应。每个请求都是独立的。

    特点

    • 无状态:每个请求都必须包含所有必要信息,服务器不会保持会话状态。
    • 最灵活也最复杂:开发者需要手动处理 JSON-RPC 消息的编码、解码、路由和错误重试,工作量很大。
    • 通用性强:几乎任何编程环境都能轻松创建 HTTP 服务。

    适用场景

    • 将现有 HTTP API 快速包装成 MCP 服务器:如果你已经有一个成熟的 RESTful API,可以编写一个薄薄的适配层将其转换为 MCP 协议。
    • 特定云环境或架构:当你的部署环境非常倾向于 HTTP 微服务时。
    • 就像通过邮寄信件问答:你(客户端)有一个问题,你写一封信(HTTP请求),寄到一个地址(URL)。对方(服务器)收到信,处理好问题,再写一封回信(HTTP响应)寄给你。每次问答都需要一次完整的邮寄过程。

MCP客户端

桌面客户端

使用桌面应用来连接mcp服务器的方式都称为桌面客户端。例如上文中演示用的VSCode和下面介绍的cursor和Cherry Studio都是桌面客户端。

cursor作为客户端

在cursor中配置mcp的步骤:

  1. 打开cursor -> open settings -> MCP & Integrations -> New MCP Server
  2. 编辑mcp.json
    以sse方式为例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    {
    "mcpServers": {
    "mcp-server-demo": {
    "name": "MCP 服务器",
    "type": "sse",
    "description": "测试用mcp服务器",
    "isActive": true,
    "timeout": "34",
    "url": "http://127.0.0.1:8000/sse"
    }
    }
    }
    注意与在VSCode中不太一样,最外层由servers改为mcpServers

Cherry Studio作为客户端

设置 -> MCP -> 添加服务器
支持三种方式添加:

  • 快速创建,手动填写选项框配置的方式。
  • 从JSON导入,一般适合别的地方配置过mcp.json文件,直接将mcp.json文件内容粘贴过来,注意最外层是mcpServers,与Cursor中配置相同,与VSCode的mcp.json有一点点差别。
  • 导入DXT包,下载DXT包导入,没试过。

快速创建最后其实也是自动生成一个json,这个json可以直接复制到Cursor中用,或者改一改用在VSCode中。

添加后点击左侧mcp名称右边的🖊图标可以编辑配置或查看json格式配置。

编写MCP客户端

参考 官网示例
参考 MCP python-sdk

我们以stdio方式为例看看如何使用python-sdk编写MCP客户端。

连接配置

我们需要先准备MCP服务器的启动参数:

1
2
3
4
5
6
7
from mcp import StdioServerParameters

# Create server parameters for stdio connection
server_params = StdioServerParameters(
command="uv", # Using uv to run the mcp server
args=["run", "server.py"], # Make sure that we're already in project root dir
)

可以自己先验证一下,例如在命令行运行此命令uv run server.py,没有报错说明可行(server.py对应你自己定义的MCP服务文件)。

之后需要启动一个客户端并建立与MCP服务的连接:

1
2
3
4
from mcp.client.stdio import stdio_client

async with stdio_client(server_params) as (read, write):

在这个过程中stdio_client(server_params) 会建立 MCP 客户端与服务端之间的异步通信流对象read,write,分别用于读取服务端发来的消息(输入流)和发送消息到服务端(输出流)。

底层通过标准输入输出(stdio)实现进程间通信,在 MCP 协议中,客户端和服务端通过这两个流异步收发 JSON 消息,实现功能调用和数据交换。

想要客户端和服务端进行一系列交互(如 prompt、tool、resource 的调用)还需要通过ClientSession 创建一个 MCP 协议的客户端会话(session):

1
2
3
4
5
from mcp import ClientSession

async with ClientSession(read, write) as session:
# Initialize the connection
await session.initialize()

其中session就是MCP客户端和服务端之间的会话对象

通过 session 可以调用 MCP 协议的各种方法(如 list_promptsget_promptcall_tool 等),自动处理消息收发、状态管理和资源释放。

查看和获取资源

使用list_resources()read_resource():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# List available resources. (only list static resources)
resources = await session.list_resources()
print(f"Available resources: {[r.uri for r in resources.resources]}")

# Read a static resource
resource_content = await session.read_resource(AnyUrl("lunch://suggestions"))
content_block = resource_content.contents[0]
if isinstance(content_block, types.TextResourceContents):
print(f" Static Resource content: {content_block.text}")

# Static Resource content: Here are some lunch suggestions:
# - Italian: Pasta, Pizza
# - Chinese: Dumplings, Stir-fry
# - Mexican: Tacos, Burrito
# - Japanese: Sushi, Ramen
# - Indian: Curry, Biryani

注意:list_resources()展示的只是定义的静态资源,例如使用@mcp.resource("lunch://suggestions")这种固定访问URL的资源,至于例子中的@mcp.resource("lunch://suggestion/{cuisine}")修饰的资源为动态资源,不能通过list_resources()查看。

如果想要让LLM也知道都有哪些动态资源和访问方式,可以采用下面两种方式:

  1. 服务端主动暴露资源说明
  • 可以在服务端增加一个静态资源(如 resource://help),返回所有动态资源的 URI 模板和参数说明,客户端读取即可。
  • 例如:
    1
    2
    3
    4
    @mcp.resource("lunch://help")
    def lunch_help() -> str:
    return "动态资源: lunch://suggestion/{cuisine},参数: cuisine=italian/chinese/..."

  1. 通过 prompt 或工具提示
  • 服务端可以通过 prompt 或工具接口,返回支持的动态资源列表或参数枚举。

如果要访问动态资源,只需要将动态部分补充到URL中:

访问动态资源
1
2
3
4
5
6
# Read a dynamic resource
resource_content = await session.read_resource(AnyUrl("lunch://suggestion/italian"))
content_block = resource_content.contents[0]
if isinstance(content_block, types.TextResourceContents):
print(f"Dynamic Resource content: {content_block.text}") # Dynamic Resource content: How about some pasta or pizza?

查看和调用工具

使用list_tools()call_tool()

查看和调用工具
1
2
3
4
5
6
7
8
9
10
11
12
# List available tools
tools = await session.list_tools()
print(f"Available tools: {[t.name for t in tools.tools]}") # Available tools: ['choose_lunch']

# Call a tool (choose_lunch tool from lunch-helper)
result = await session.call_tool("choose_lunch", arguments={"cuisine": "italian"})
result_unstructured = result.content[0]
if isinstance(result_unstructured, types.TextContent):
print(f"Tool result: {result_unstructured.text}") # Tool result: How about some pasta or pizza?
result_structured = result.structuredContent
print(f"Structured tool result: {result_structured}") # Structured tool result: {'result': 'How about some pasta or pizza?'}

对于结果,既有非结构化的字符串格式,也有结构化的返回方式。

查看和获取提示词

使用list_prompts()get_prompt()

查看和获取提示词
1
2
3
4
5
6
7
8
9
# List available prompts
prompts = await session.list_prompts()
print(f"Available prompts: {[p.name for p in prompts.prompts]}")

# Get a prompt (greet_user prompt from MCP-server)
if prompts.prompts:
prompt = await session.get_prompt("greet_user", arguments={"name": "Alice", "style": "friendly"})
print(f"Prompt result: {prompt.messages[0].content}")

这里展示了prompts列表,并调用了一个需要namestyle两个请求参数的prompt。

注意这几个方法都是异步的,因此获取结果时要使用await关键词,定义函数时要使用关键词async

完整示例

个人仓库示例 https://gitee.com/peakofmountains/choose-lunch-mcp

其他

在命令行python没法用

如果确实安装了python,但是在命令行使用python命令进不去python环境,可能是因为没有将python的安装目录添加到环境变量中,请先编辑环境变量,将python的安装目录添加到用户的Path环境变量中,这样在任何命令行目录直接使用python命令就相当于进入python的安装目录然后使用python命令,方便很多。

注意,windows上有的人会出现使用python命令会跳转Microsoft Store,这是因为命令行参数中C:\Users\xxx\AppData\Local\Microsoft\WindowsApps
当然,有的人为了保证本机的环境隔离或者其他考量,不想添加到环境变量中或者使用conda虚拟环境等就可以,只要切换到能使用python命令的环境中即可。

编写约定规范

官方提供了两种开发文档,其一是完整mcp规范文档,可以用来喂给大模型让大模型学会按照此规范开发mcp服务器代码。还有一种是Python SDK,这个是Python实现的工具包。

两者的关系是:官方文档定义了 MCP 的协议规范。它是一份技术标准文档,SDK(Software Development Kit)是一个代码库,是 Anthropic 官方提供的、用于帮助 Python 开发者更轻松地构建 MCP Server 的工具,让你不需要从零开始实现协议规范,极大地提高了开发效率和可靠性,避免出错。

建议先快速通读 官方文档 的 Overview 和 Concepts,理解 MCP 的核心思想(Server, Client, Tools, Resources, Prompts)后阅读 Python SDK 并实践快速创建项目。