为什么需要MCP
众所周知,当代的大模型依然存在着或多或少的幻觉问题,大模型只保证能回答你的问题,但不保证回答的正确性,在越是专业的问题上,幻觉问题越严重。
为了解决这个问题,现在的技术方案主要有两种:大模型微调、RAG 。其中,最常用的技术方案就是RAG。RAG通过为大模型外挂知识库,旨在通过从外部知识库中检索相关信息来增强模型生成的内容准确性。 这些知识库来源十分广泛,可以是结构化的数据库、非结构化的文本集合、维基百科、专业文献,甚至是外部API。
但多种多样的知识库来源也引入了新的问题,即不同知识库的访问方式不同 。假设存在M个大模型,N个外部知识库,在这种情况下,我们为每个大模型接入每个外部知识库时都需要编写一套代码。复杂度达到了 M x N
。
为了解决上述问题,MCP协议应运而生。在该协议下,大模型不再与外部知识库直接相连,而是借由MCP客户端与MCP服务器与外部知识库通信。每个大模型连接MCP客户端,MCP服务器连接每个外部知识库,从而将复杂度降低到了 M + N
。

该协议本身并不复杂,甚至可能许多公司在此之前已经研发过自己的"MCP"协议
MCP官方定义
MCP(模型上下文协议)是一个使大型语言模型(LLMs,如 Claude)能够与外部工具和数据源交互的协议。使用 MCP,您可以:
- 构建为 LLMs 提供工具和数据的服务器
- 将这些服务器连接到兼容 MCP 的客户端
- 通过自定义功能扩展 LLM 的能力
MCP系统结构

从图中可以看到,MCP的系统协议架构主要包括MCP Hosts
、MCP Client
、MCP Server
- MCP Host(主机应用) :Hosts 是指 LLM 启动连接的应用程序,像Cursor、Claude、Cline 这样的应用程序。
- MCP Client(客户端) :客户端是用来在 Hosts 应用程序内维护与 Server 之间 1:1 连接。一个主机应用中可以运行多个MCP客户端,从而同时连接多个不同的服务器。
- MCP Server(服务器) :独立运行的轻量程序,通过标准化的协议,为客户端提供上下文、工具和提示,是MCP服务的核心。
我们可以通过HTTP来理解MCP
scss
MCP Host(MCP Client) <--MCP--> MCP Server(Services)
Chrome(Http Client) <--HTTP--> Http Server(Resources)
MCP Hello World
根据上面的理解,实际上利用MCP开发,就和利用Http协议开发一样的逻辑。下面将使用示例来展示MCP的Hello World
MCP Server
为了更直观的体验MCP,本文暂时先使用已经支持了MCP Client
的程序来充当Host
进行测试,就像我们开发HTTP Server
时直接使用Chrome进行测试,而不另开发HTTP Client
一样。
虽然官方使用Claude客户端来充当Host,但由于Claude并不支持国区,故本文使用已经支持的DeepChat代替Claude客户端。
导入 SDK
官方中文教程给的Java版的SDK似乎有点问题,这里改用了最新的英文原版提供的Spring版的SDK。
xml
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
</dependencies>
编写工具
使用@Serevice
编写服务层,这里面用于定义有哪些工具
java
@Service // 服务层
public class WeatherService {
// 响应模板,自然语句格式
private static final String RESPONSE_TEMPLATE = """
%s的天气为%s, 气温为%s
""";
private final RestClient restClient;
public WeatherService() {
String apiUrl = "http://apis.juhe.cn/simpleWeather/query";
this.restClient = RestClient.builder()
.baseUrl(apiUrl)
.defaultHeader("Content-Type", "application/x-www-form-urlencoded")
.defaultHeader("Accept", "application/json")
.defaultHeader("User-Agent", "WeatherApiClient/1.0")
.build();
}
// LLM可用的工具,要准确描述该工具的作用,用于LLM的查找
@Tool(description = "获得中国的天气信息")
public String getWeather(
@ToolParam(description = "城市") String city
) throws UnsupportedEncodingException {
byte[] bytes = city.getBytes("GBK");
String u8City = new String(bytes, StandardCharsets.UTF_8);
String apiKey = "your api key";
RestClient.ResponseSpec retrieve = this.restClient.get()
.uri("?key=" + apiKey + "&city=" + u8City)
.retrieve();
WeatherPojo body = retrieve.body(WeatherPojo.class);
try {
return String.format(RESPONSE_TEMPLATE,
u8City,
body.getResult().getRealtime().getInfo(),
body.getResult().getRealtime().getTemperature()
);
} catch (NullPointerException e) {
return "抱歉,我无法获取到" + u8City + "的天气信息";
}
}
}
byte[] bytes = city.getBytes("GBK");
String u8City = new String(bytes, StandardCharsets.UTF_8);
由于默认使用stdio的方式传输,而客户端往IO管道里放数据时使用UTF-8模式encode,而OS(windows)再decode时使用GBK。导致产生乱码,所以这里我们需要把GBK再转回UTF-8,
创建Bean
主要是为了将API的JSON返回值转成Bean
java
@Data
public class WeatherPojo {
private String reason;
private Result result;
private int errorCode;
@Data
public static class Result {
private String city;
private Realtime realtime;
@Data
public static class Realtime {
private String temperature;
private String humidity;
private String info;
private String wid;
private String direct;
private String power;
}
}
}
将Tool
注册进入Spring
容器
java
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder().toolObjects(weatherService).build();
}
请求访问
首先,需要在客户端这里配置服务器信息。
json
{
"mcpServers": {
"mcp-server": {
"command": "java",
"args": [
"-Dspring.ai.mcp.server.transport=STDIO",
"-jar",
"D:/Documents/IdeaProjects/mcp-server/target/mcpserver-0.0.1-SNAPSHOT.jar"
]
}
}
}
然后我们直接对话就行,deepChat软件会自动根据对话内容选择性调用Tool

结语
当前还只是最简单的一种模式,基于标准IO流进行通信,一般用于用户本地使用。不过MCP还支持Http+SSE,相比于标准IO流,这种方式可以更好的支持网络服务。说不定以后会有专门的MCP服务提供商