Appearance
MCP: Model Context Protocol
本文介绍MCP(Model Context Protocol)的基本概念,如何在Cursor中使用以及如何使用Spring AI实现MCP Server。
1. MCP介绍
1.1 概述
MCP,全称Model Context Protocol,译为模型上下文协议,是用于连接LLM和外部系统的开源协议。

MCP为LLM扩展了以下能力:
- 工具:MCP为LLM提供了额外的工具,LLM可以根据请求和上下文,调用MCP提供的工具;
- 资源:MCP为LLM提供了额外的资源(例如内部文档、数据库信息等),LLM可以根据上下文,获取这些资源;
1.2 架构
1.2.1 MCP架构介绍
在MCP架构中,存在以下三个角色:
- MCP Host:指的是AI应用程序(例如Codex、Cursor等),用于协调、管理MCP Client;
- MCP Client(客户端):用户连接MCP Server 的组件,用于从MCP Server中获取工具/资源共MCP Host使用;
- MCP Server(服务端):为MCP Client提供工具/资源的代码程序;

服务端分为本地服务端和远程服务端:
- 本地服务端:与MCP Host运行中在同一台机器上的服务端程序,与MCP Host通过STDIO交换数据;
- 远程服务端:与MCP Host运行在不同机器上的服务端程序,与MCP Host通过HTTP协议交换数据;
在MCP Client和MCP Server连接中,涉及两层架构:
- 传输层(Transport Layer):定义支持客户端和服务器之间数据交换的通信机制和通道,包括特定于传输的连接建立、消息成帧和授权;
- 数据层(Data Layer):定义基于 JSON-RPC 的客户端-服务器通信协议,包括生命周期管理和核心原语,例如工具、资源、提示和通知;
也就是说,传输层不关心数据的内容,只关心连接、发送和接收数据;数据层定义了双方对话的“语言”和“语法”。
1.2.2 传输层
传输层定义了数据如何在客户端(Client)和服务端(Server)之间物理移动。它不关心数据的内容,只关心连接、发送和接收数据。
目前 MCP 主要支持两种传输方式:
- STDIO (标准输入输出):
- 场景: MCP Server在本地运行;
- 机制: Client 启动 Server 进程,通过
stdin发送 JSON,通过stdout读取 JSON; - 特点: 极简,无需网络配置,安全性高(仅限本地);
- HTTP + SSE:
- 场景: MCP Server远程运行或云端服务;
- 机制: Client 发送
POST请求,Server 通过SSE流式返回; - 特点: 支持跨机器通信、身份验证(OAuth);
SSE是Server-Sent Event的缩写,SSE 建立在标准的 HTTP 协议之上。它的工作原理非常像“长流水”:
- 客户端发送一个普通的 HTTP 请求;
- 服务端不关闭连接,而是保持连接开启;
- 服务端以
text/event-stream的格式,源源不断地发送数据块;
1.2.3 数据层
数据层使用基于 JSON RPC 2.0 的数据交换格式,定义了数据的语法结构与语义,数据层包括以下内容:
- 生命周期管理:用于管理在客户端和服务端之间的连接,包括连接建立、能力协商、连接关闭;
- 服务端功能:使服务端能够提供核心功能,包括 AI 操作工具、上下文数据资源以及与客户端交互模板的提示;
- 客户端功能:允许服务端向客户端发送请求,包括获取用户输入、从LLM中采样、将日志消息发送到客户端;
- 其他功能:包括实时更新消息通知、长程任务进度跟踪;
关于JSON RPC:是一种轻量级的远程过程调用(Remote Procedure Call, RPC)协议,基于 JSON(JavaScript Object Notation)作为数据格式,常用于客户端与服务端之间的通信,尤其在 Web、区块链、后端服务之间非常常见。
JSON RPC的核心思想:客户端发送一个 JSON 请求,调用服务端的某个“方法”,服务端返回执行结果。
JSON RPC规定了三种基本的消息类型:
- Requests (请求): 有 ID,要求对方必须回复(如:
call_tool); - Notifications (通知): 无 ID,发完即忘(如:进度更新、资源变更通知);
- Responses (响应): 对应请求的执行结果;
更多关于JSON RPC的文档,参考资料[2]。
1.2.4 服务端
MCP Server (以下简称服务端)是一段代码,用于向LLM提供特定的能力,包括工具、资源、Prompts(提示词模板)。
工具(Tools)
工具(Tools)允许LLM执行操作,每个工具定义了一个特定的操作(例如写入数据库、修改文件、调用API等),LLM可以根据上下文调用工具。
对于工具,LLM 拥有“自主调用权”,也就是说,当连接了一个包含“工具”的 MCP Server 后,不需要用户手动点击按钮去运行工具,LLM 会在分析问题后,自发决定是否需要使用某个工具。
关于工具,服务端需要定义两个方法:
| 方法 | 作用 | 返回 |
|---|---|---|
tools/list | 列出所有的工具 | 工具定义列表 |
tools/call | 调用指定的工具 | 工具执行结果 |
例如,当调用tools/list后返回工具列表:
json
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}json
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "calculator_arithmetic",
"title": "Calculator",
"description": "Perform mathematical calculations including basic arithmetic, trigonometric functions, and algebraic operations",
"inputSchema": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "Mathematical expression to evaluate (e.g., '2 + 3 * 4', 'sin(30)', 'sqrt(16)')"
}
},
"required": ["expression"]
}
},
{
"name": "weather_current",
"title": "Weather Information",
"description": "Get current weather information for any location worldwide",
"inputSchema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name, address, or coordinates (latitude,longitude)"
},
"units": {
"type": "string",
"enum": ["metric", "imperial", "kelvin"],
"description": "Temperature units to use in response",
"default": "metric"
}
},
"required": ["location"]
}
}
]
}
}当调用tools/call用于执行工具:
json
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "weather_current",
"arguments": {
"location": "San Francisco",
"units": "imperial"
}
}
}json
{
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "Current weather in San Francisco: 68°F, partly cloudy with light winds from the west at 8 mph. Humidity: 65%"
}
]
}
}资源(resources)
资源(resources)提供了额外的信息,可以帮助LLM更全面的理解问题。
注意,资源的加载是由MCP Host基于规则或用户选择来控制的,也就是说,当连接上提供资源的MCP Server后,并不是由LLM直接访问资源,而是由MCP Host(也就是AI应用程序)获取相关资源,然后提供给LLM。因此,从大量外部资源中,选择与当前问题最相关的资源提供给LLM,是MCP Host的责任。
在MCP中,每个资源都有唯一的标识符,并且声明了数据格式(MIME Type),有了这两者,就可以解决如何发现资源以及如何解析资源的问题。
MCP把资源分为两类:
- Direct Resources(静态资源):URI 是固定的,不带参数;例如
calendar://events/2024,用于获取2024 年的所有日历事件; - Resource Templates(动态资源):URI 是模板,支持参数占位符(如
{city});例如travel://activities/{city}/{category},用于获取Barcelona 的 museums 类型活动;
关于资源,MCP Server定义了以下方法:
| 方法 | 作用 | 返回 |
|---|---|---|
resources/list | 获取所有静态资源 | 静态资源列表 |
resources/templates/list | 获取所有动态资源 | 动态资源列表 |
resources/read | 获取资源内容 | 带元数据的资源内容 |
resources/subscribe | 订阅资源变动 | 订阅结果 |
Prompts(提示词模板) Prompts 在 MCP 中是“用户触发的任务模板 + 上下文构造器”。它把零散的自然语言需求转为结构化参数,并在服务端编排 Resources(取数)/Tools(能力),生成标准化的 messages(system/user 等),从而为 LLM 提供稳定、可复用的高质量输入,提升结果一致性与可控性。
对于Prompts,必须是由用户显式触发。
对于Prompts,MCP Server提供两个方法:
| 方法 | 作用 | 返回 |
|---|---|---|
prompts/list | 列出所有的提示词模板 | 提示词模板列表 |
prompts/get | 获取某个具体的提示词内容 | 完整的提示词 |
举一个例子说明在MCP中,Prompts(提示词模板)的作用
假设用户直接输入“帮我规划一个去巴塞罗那的旅行”给LLM,此时LLM规划的结果可能不准确。
而用户通过直接调用提示词模板,此时会要求输入关键信息,然后,在MCP Server内部,可能会获取相关信息/执行特定工作,然后返回经过精心设计的提示词,例如:
Details
typescript
server.handle("prompts/get", async ({ name, arguments: args }) => {
if (name === "plan-vacation") {
// 1️⃣ 调用 Resource(获取城市信息)
const cityInfo = await server.callResource(
`travel://city/${args.destination}`
);
// 2️⃣ 调用 Resource Template(获取活动)
const activities = await server.callResource(
`travel://activities/${args.destination}`
);
// 3️⃣ 组合 Prompt
return {
messages: [
{
role: "system",
content: "You are a professional travel planner"
},
{
role: "user",
content: `
Plan a ${args.duration}-day trip to ${args.destination}.
City info:
${cityInfo}
Activities:
${activities}
Budget: ${args.budget}
Interests: ${args.interests}
`
}
]
};
}
});此时再将MCP Server返回的完整提示词发送给LLM,之后LLM再进行规划,就会更准确。
简单概括Prompts用法
- AI 程序通过
prompts/list发现可用模板; - 用户选择模板并填写参数(有类型、必填校验与补全);
- 调用
prompts/get; - MCP Server 解析参数→可选调用资源/工具→拼装
messages返回; - AI 程序 将
messages发送给 LLM 执行;
1.2.5 客户端
除了MCP 服务端可以提供功能,在MCP设计中,客户端也可以提供功能,主要有以下三个功能:
- Roots(本地文件范围):告诉 Server:“你可以访问哪些本地文件目录”,形式:
file://路径; - Sampling(让 Server 请求 LLM):Server 可以“反过来请求 Client 调用 LLM”,Client 控制模型调用(成本/权限在 Client);
- Elicitation(反向向用户提问):Server 可以请求 Client,“问用户一个问题”或“让用户补充信息”;
由于客户端能力目前使用率较低,不过多介绍。
1.3 工作流程
本小节简单概述一下MCP的工作流程,主要分为以下步骤:启动、发现、交互
连接与初始化:
MCP Host 启动: 用户打开AI程序(即MCP Host),Host 读取配置文件(例如
mcp_config.json);创建 Server 进程: AI程序根据配置启动 Server 进程(本地 STDIO)或建立网络连接(远程 HTTP/SSE);
建立连接: AI程序为每个Server初始化Client组件,Client 发起
initialize请求,双方交换版本信息和各自支持的功能(Capabilities,例如是否支持 Sampling 或 Roots);能力发现:
扫描资源与工具: Client 向 Server 请求可用列表:
list_resources: 看看有哪些只读数据;list_tools: 看看有哪些可执行的函数;list_prompts: 看看有哪些预设的指令模板;
交互
用户提问: 用户说:“分析一下本地项目里的
UserService.java代码”;Host 调度: MCP Host 识别到用户意图涉及特定资源,主动通过 Client 发送
resources/read请求给Local MCP Server,获取代码内容;上下文注入: MCP Host 将获取的资源内容合并到用户的提问中,一起发给 LLM;
LLM 决策: LLM 判断需要读取数据库,发出指令:“
tool_call”;**Host 调度:**MCP Host接受到LLM返回的工具调用响应,找到对应的Remote MCP Server提供的工具(也有可能是其他工具)并执行,并且将执行结果返回给LLM;
2. 在Cursor中使用MCP
本小节介绍如何在Cursor中使用MCP。
2.1 配置MCP Server
首先需要在Cursor中添加MCP Servers,即在mcp.json中添加,例如:
添加本地MCP Server,并且是以npx命令启动:
json{ "mcpServers": { "server-name": { "command": "npx", "args": ["-y", "mcp-server"], "env": { "API_KEY": "value" } } } }添加本地MCP Server,并且是以python命令启动:
json{ "mcpServers": { "server-name": { "command": "python", "args": ["mcp-server.py"], "env": { "API_KEY": "value" } } } }添加远程MCP Server:
json// MCP server using HTTP or SSE - runs on a server { "mcpServers": { "server-name": { "url": "http://localhost:3000/mcp", "headers": { "API_KEY": "value" } } } }
根据mcp.json的位置不同,MCP Server所生效的范围也不同:
- 当前目录下的
.cursor/mcp.json:项目范围; ~/.cursor/mcp.json:用户目录下,全局范围;
对于本地MCP Server,可以配置以下参数:
| 参数 | 是否必须 | 描述 | 示例 |
|---|---|---|---|
| type | Yes | 连接类型 | "stdio" |
| command | Yes | 用于启动本地MCP Server的命令(在本地环境可用) | "npx", "node", "python", "docker" |
| args | No | 启动参数 | ["server.py", "--port", "3000"] |
| env | No | 环境变量 | {"API_KEY": "${env:api-key}"} |
| envFile | No | 环境变量文件 | ".env", "${workspaceFolder}/.env" |
在MCP Server配置中,command, args, env, url,和 headers参数值,可以使用占位符,Cursor提供了以下占位符:
${env:NAME}:环境变量;${userHome}:用户主目录;${workspaceFolder}:工作区目录 (也就是包含.cursor/mcp.json的目录);${workspaceFolderBasename}:工作区名称;${pathSeparator}and${/}:操作系统路径分隔符;
例如:
json
{
"mcpServers": {
"local-server": {
"command": "python",
"args": ["${workspaceFolder}/tools/mcp_server.py"],
"env": {
"API_KEY": "${env:API_KEY}"
}
}
}
}例如,在Cursor中配置postgres MCP Server:
json
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgresql://localhost/demo"
]
}
}
}然后在Cursor Setting -> Tools & MCPs中,就能看到安装的MCP Servers了,并且每个MCP Server有哪些工具、资源、Prompt都会显示:

2.2 使用MCP Server
当我们在对话框中向LLM发出请求时,LLM会利用安装的MCP工具,进行查询:

默认情况下,执行MCP提供的工具,需要人工确认。
3. 自定义MCP
3.1 STDIO MCP Server
在本小节中介绍如何利用 Spring AI 实现自己的MCP Server,关于在本地运行的天气服务。
TIP
如果要实现基于STDIO的MCP Server,不要在代码中使用System.out.println()或System.out.print(),因为这些方法会直接写入标准输出,会扰乱JSON RPC信息。
首先在pom.xml中引入以下依赖:
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server</artifactId>
<version>2.0.0-M5</version>
</dependency>然后修改添加如下配置:
properties
spring.ai.mcp.server.stdio=true
spring.main.web-application-type=none
spring.main.banner-mode=off
logging.pattern.console=spring.ai.mcp.server.stdio=true:显式开启 MCP 的 stdio 传输模式;spring.main.web-application-type=none:禁用 Web 容器,在 stdio 模式下,不需要监听任何端口;spring.main.banner-mode=off:彻底关闭 Spring 启动时的字符横幅,因为Spring启动时字符横幅时输出到标准输出的,所以在STDIO模式下会对MCP通讯有影响;logging.pattern.console=:清空控制台日志的输出,同样也是避免输出到标准输出,对MCP有影响;
然后创建工具:
java
@Service
public class WeatherService {
public WeatherService() {
}
@Tool(description = "Get weather forecast for a specific latitude/longitude")
public String getWeatherForecastByLocation(
double latitude, // Latitude coordinate
double longitude // Longitude coordinate
) {
return "多云";
}
@Tool(description = "Get weather alerts for a US state")
public String getAlerts(
@ToolParam(description = "Two-letter US state code (e.g. CA, NY)") String state
) {
return "暴雨预警";
}
}@Tool:定义了工具;
注册工具:
java
@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();
}
}之后,运行mvn clear install命令,在target目录下找到打包好的jar包,然后在Cursor中配置:
json
{
"mcpServers": {
"weather": {
"command": "java",
"args": [
"-jar",
"/absolute-path/mcp-server/target/mcp-server-0.0.1-SNAPSHOT.jar"
]
}
}
}
在对话框中试验如下:

3.2 SSE MCP Server
要将STDIO的MCP Server换成SSE MCP Server,首先将依赖换成如下依赖:
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
<version>2.0.0-M5</version>
</dependency>然后删除以下关于STDIO的配置:
properties
#spring.ai.mcp.server.stdio=true
#spring.main.web-application-type=none
#spring.main.banner-mode=off
#logging.pattern.console=这样就转换完成了,直接启动项目:

在Cursor中配置如下:
json
{
"weather": {
"url": "http://localhost:8080/sse"
}
}
}即可正常使用。
参考资料
[1] MCP:https://modelcontextprotocol.io/docs/getting-started/intro
[2] JSON RPC: https://www.jsonrpc.org/specification
[3] Cursor MCP:https://cursor.com/docs/mcp
[4] Spring AI MCP:https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html