MCP模型上下文协议
官网地址: modelcontextprotocol.io/docs/gettin…
spring ai alibaba官网: java2ai.com/docs/1.0.0.…
mcp 模型地址: mcp.so/zh
痛点?为什么出现MCP
MCP准备取代工具调用(ToolCalling)
ToolCalling痛点: 需要为每个工具 单独开发接口,导致重复劳动
痛点:
- 模型之间调用遵循什么协议?
- 提供一个服务 (百度地铁MCP 一个服务可以看路线/规划/时间/坐标等信息)
MCP入门概念
MCP是一种开放协议,标准化应用程序如何向大模型(LLMS)提供上下文。MCP提供了一种标准化的方式将AI模型连接到不同的数据源和工具。大模型版本的openFeign。
MCP架构
CS 客户端-服务器架构
Spring AI MCP 采用模块化架构,包括以下组件:
- Spring AI 应用程序:使用 Spring AI 框架构建想要通过 MCP 访问数据的生成式 AI 应用程序
- Spring MCP 客户端:MCP 协议的 Spring AI 实现,与服务器保持 1:1 连接
- MCP 服务器:轻量级程序,每个程序都通过标准化的模型上下文协议公开特定的功能
- 本地数据源:MCP 服务器可以安全访问的计算机文件、数据库和服务
- 远程服务:MCP 服务器可以通过互联网(例如,通过 API)连接到的外部系统
支持两种协议: SSE / STDIO
本地MCP开发步骤
本地MCP 服务端实现
建Module
POM文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.miao</groupId>
<artifactId>SpringAIAlibaba-test01</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>SAA-11MCPServer</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 注意事项【重要】
spring-ai-starter-mcp-server-webflux 这个依赖 只能用在 spring-boot-starter 不能用在 spring-boot-starter-web 上,
否则会使用tomcat启动,而不是netty启动,从而导致mcpserver启动失败,但是程序正常运行,mcp服务连接不上
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
<!-- 模型服务灵积 调用alibaba生态的协议 对标openai协议 -->
<!-- <dependency>-->
<!-- <groupId>com.alibaba.cloud.ai</groupId>-->
<!-- <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>-->
<!-- <version>1.0.0.2</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.38</version>
</dependency>
</dependencies>
</project>
yml配置
server:
port: 8014
servlet:
encoding:
enabled: true
force: true
charset: UTF-8
spring:
application:
name: SAA-11MCPServer
ai:
mcp:
server:
type: async
name: mcp-server-miao
version: 1.0.0
业务提供WeatherService
package com.miao.service;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class WeatherService {
@Tool(description = "获取指定位置的天气信息", returnDirect = false)
public String getWeather(String location) {
// 模拟获取天气信息的逻辑
Map<String, String> weatherData = Map.of(
"北京", "晴天,温度20摄氏度",
"上海", "多云,温度22摄氏度",
"广州", "小雨,温度28摄氏度"
);
return weatherData.getOrDefault(location, "未知位置,无法获取天气信息");
}
}
配置类 注册mcpService 暴露出去
package com.miao.config;
import com.miao.service.WeatherService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class McpServerConfig {
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder()
.toolObjects(weatherService)
.build();
}
}
本地客户端实现
建立module
POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.miao</groupId>
<artifactId>SpringAIAlibaba-test01</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>SAA-12MCPClient</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mcp client依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
<!-- 模型服务灵积 调用alibaba生态的协议 对标openai协议 -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>1.0.0.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.38</version>
</dependency>
</dependencies>
</project>
YML
# 服务器配置
server.port=8082
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
# Spring应用配置
spring.application.name=SAA-12
# Spring AI 通义千问配置
spring.ai.dashscope.api-key=${qwen-api-key}
spring.ai.dashscope.url=https://dashscope.aliyuncs.com/compatible-mode/v1
spring.ai.dashscope.chat.options.model=qwen-flash
# Spring AI MCP客户端配置
spring.ai.mcp.client.type=async
spring.ai.mcp.client.request-timeout=60000
spring.ai.mcp.client.toolcallback.enabled=true
spring.ai.mcp.client.see.connections.server1.url=http://localhost:8014
启动类
package com.miao;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SAA12MCPClientApplication {
public static void main(String[] args) {
SpringApplication.run(SAA12MCPClientApplication.class, args);
}
}
业务配置类
package com.miao.config;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.mcp.AsyncMcpToolCallbackProvider;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SaaLLMConfig {
/**
* 方式一 : 通过配置文件注入 API Key
*/
@Value("${spring.ai.dashscope.api-key}")
private String apiKey;
@Bean
public DashScopeApi dashScopeApi() {
return DashScopeApi.builder()
// 从系统环境变量中读取配置
.apiKey(System.getenv("qwen-api-key"))
.build();
}
@Bean
public ChatClient chatClient(ChatModel chatModel, ToolCallbackProvider tools) {
return ChatClient.builder(chatModel)
.defaultToolCallbacks(tools.getToolCallbacks()) //mcp协议
.build();
}
}
业务controller
package com.miao.controller;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
public class McpClientController {
@Resource
private ChatModel dashScopeChatModel;
@Resource
private ChatClient chatClient;
// chatClient方式调用mcp服务端
@GetMapping("/chatClient")
public Flux<String> chat(@RequestParam(name = "msg", defaultValue = "北京") String msg) {
System.out.println("chatClient方式调用mcp服务端");
return chatClient.prompt(msg).stream().content();
}
// chatModel方式未调用mcp服务端
@GetMapping("/chatModel")
public String chatWithModel(@RequestParam(name = "msg", defaultValue = "北京") String msg) {
System.out.println("chatModel方式未调用mcp服务端");
return dashScopeChatModel.call(msg);
}
}
百度地图开发
mcp官网: mcp.so/zh/server/b…
环境配置
第一步: 申请百度api-key
百度地图mcp的github地址: github.com/baidu-maps/…
百度api申请地址: lbsyun.baidu.com/apiconsole/…
创建module
POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.miao</groupId>
<artifactId>SpringAIAlibaba-test01</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>SAA-12MCPClient</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mcp client依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
<!-- 模型服务灵积 调用alibaba生态的协议 对标openai协议 -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
<version>1.0.0.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.38</version>
</dependency>
</dependencies>
</project>
YML
# 服务器配置
server.port=8082
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
# Spring应用配置
spring.application.name=SAA-12
# Spring AI 通义千问配置
spring.ai.dashscope.api-key=${qwen-api-key}
spring.ai.dashscope.url=https://dashscope.aliyuncs.com/compatible-mode/v1
spring.ai.dashscope.chat.options.model=qwen-flash
# Spring AI MCP客户端配置
spring.ai.mcp.client.type=async
spring.ai.mcp.client.request-timeout=60000
spring.ai.mcp.client.toolcallback.enabled=true
spring.ai.mcp.client.stdio.servers-configuration=classpath:/npm-server.json
npm-server.json内容
自己本地需要按照node,才能成功运行
{
"mcpServers": {
"baidu-map": {
"command": "cmd",
"args": [
"/c",
"npx",
"-y",
"@baidumap/mcp-server-baidu-map"
],
"env": {
"BAIDU_MAP_API_KEY": "api-key"
}
}
}
}
主启动类
package com.miao;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SAA12MCPClientApplication {
public static void main(String[] args) {
SpringApplication.run(SAA12MCPClientApplication.class, args);
}
}
SaaLLMConfig配置类
package com.miao.config;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.mcp.AsyncMcpToolCallbackProvider;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SaaLLMConfig {
/**
* 方式一 : 通过配置文件注入 API Key
*/
@Value("${spring.ai.dashscope.api-key}")
private String apiKey;
@Bean
public DashScopeApi dashScopeApi() {
return DashScopeApi.builder()
// 从系统环境变量中读取配置
.apiKey(System.getenv("qwen-api-key"))
.build();
}
@Bean
public ChatClient chatClient(ChatModel chatModel, ToolCallbackProvider tools) {
return ChatClient.builder(chatModel)
.defaultToolCallbacks(tools.getToolCallbacks()) //mcp协议
.build();
}
}
controller测试
package com.miao.controller;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
public class McpClientController {
@Resource
private ChatModel dashScopeChatModel;
@Resource
private ChatClient chatClient;
// chatClient方式调用mcp服务端
@GetMapping("/chatClient")
public Flux<String> chat(@RequestParam(name = "msg", defaultValue = "北京") String msg) {
System.out.println("chatClient方式调用mcp服务端");
return chatClient.prompt(msg).stream().content();
}
}