Spring AI Alibaba - MCP入门

MCP(Model Context Protocol)模型上下文协议

MCP:标准化了应用程序如何像LLM提供上下文。提供了一种标准化的方式将AI模型连接到不同的数据源和工具。

可以将MCP想象成AI应用的USB-C端口,USB-C提供了标准化方式将设备连接到外围设备,MCP提供一种标准化方式将AI模型连接到不同的数据源和工具。

解决多个大模型调用同一个外部工具的需求。

无标题20

无标题21

无标题18

MCP.so - MCP 应用市场

MCP的两种协议,一般使用STDIO。因为是双向流

无标题19

MCP架构

MCP主机:发起请求的AI程序,比如聊天机器人

MCP客户端:在主机程序内部,与MCP服务器保持1:1的连接

MCP服务器:为MCP客户端提供上下文、工具和提示信息。

本地资源:本地计算机中可供MCP服务器安全访问的资源,如文件

远程资源:MCP服务器可以远程连接到的资源,如通过API提供的数据。

开发者只需写一次MCP服务,所有兼容MCP协议模型都能调用,MCP让大模型从“被动应答”变为“主动调用工具”。

调用一个MCP服务器,就等价调用了带有多个功能的utils工具类。

代码演示

MCP服务端

  1. pom依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!--spring-ai-starter-mcp-server-webflux 不能和spring-boot-starter-web 依赖并存,
        否则会使用tomcat启动而不是netty。从而导致mcpserver启动失败。但程序正常运行,mcp客户端连不上-->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
    
  2. 配置文件

    server.port=8013
    # ?????????
    server.servlet.encoding.charset=UTF-8
    server.servlet.encoding.enabled=true
    server.servlet.encoding.force=true
    
    spring.application.name=saa14-localmcpserver
    
    # mcp server configuration
    # ????
    spring.ai.mcp.server.type=async
    spring.ai.mcp.server.name=customer-define-mcp-server
    spring.ai.mcp.server.version=1.0.0
    
  3. 配置类

    @Configuration
    public class McpServerConfig {
    
        /**
         * 将工具方法暴露给外部的MCP client 调用
         * @param weatherService
         * @return
         */
        @Bean
        public ToolCallbackProvider weatherTools(WeatherService weatherService) {
            return MethodToolCallbackProvider.builder()
                    .toolObjects(weatherService)
                    .build();
        }
    }
    
  4. 服务类

    @Service
    public class WeatherService {
    
        @Tool(description = "根据城市名称获取天气信息")
        public String getWeather(String location) {
            Map<String, String> map = new HashMap<String, String>();
            map.put("北京", "最近下雨很多");
            map.put("上海", "一直都是多云");
            map.put("深圳", "阴晴不定的");
            return map.getOrDefault(location, "未知城市");
        }
    }
    

MCP客户端

  1. pom依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
    
  2. 配置文件

    server.port=8014
    # ?????????
    server.servlet.encoding.charset=UTF-8
    server.servlet.encoding.enabled=true
    server.servlet.encoding.force=true
    
    spring.application.name=saa15-localmcpclient
    
    #springai ????config
    spring.ai.dashscope.api-key=${aliQwen-apikey}
    
    # mcp client configuration
    spring.ai.mcp.client.type=async
    spring.ai.mcp.client.request-timeout=60s
    spring.ai.mcp.client.toolcallback.enabled=true
    spring.ai.mcp.client.sse.connections.mcp-server1.url=http://localhost:8013
    
  3. 配置类

    @Configuration
    public class SaaLLMConfig {
    
        private final String DEEPSEEk_MODEL="deepseek-v3";
        private final String QWEN_MODEL="qwen-max";
    
        // 方式一
        @Value("${spring.ai.dashscope.api-key}")
        private String apiKey;
    
        // 配置大模型对外暴露的api
        @Bean(name = "deepseekChatModel")
        public ChatModel deepseekChatModel() {
        return DashScopeChatModel.builder()
                // 配置apikey
                .dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
                // 配置模型
                .defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEk_MODEL).build())
                .build();
        }
    
        @Bean(name = "qwenChatModel")
        public ChatModel qwenChatModel() {
            return DashScopeChatModel.builder()
                    // 配置apikey
                    .dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
                    // 配置模型
                    .defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build())
                    .build();
        }
    
        @Bean(name = "deepseekChatClient")
        public ChatClient deepseekChatClient(@Qualifier("deepseekChatModel") ChatModel deepseekChatModel) {
            return ChatClient.builder(deepseekChatModel)
                    .build();
        }
    
        @Bean("qwenChatClient")
        public ChatClient qwenChatClient(@Qualifier("qwenChatModel") ChatModel qwenChatModel, ToolCallbackProvider toolCallbackProvider) {
            return ChatClient.builder(qwenChatModel)
                    .defaultToolCallbacks(toolCallbackProvider.getToolCallbacks()) // mcp server端暴露的工具方法
                    .build();
        }
    }
    
  4. 控制器类

    @RestController
    public class McpClientController {
    
        @Resource(name = &quot;qwenChatModel&quot;)
        private ChatModel qwenChatModel;
    
        @Resource(name = &quot;qwenChatClient&quot;)
        private ChatClient qwenChatClient;
    
        /**
         * 使用mcp server端暴露的工具方法
         * @param msg
         * @return
         */
        @GetMapping(&quot;/mcpclient/chat&quot;)
        public Flux&lt;String&gt; chat(@RequestParam(name=&quot;msg&quot;,defaultValue = &quot;北京&quot;) String msg) {
            return qwenChatClient.prompt(msg).stream().content();
        }
    
        /**
         * 不使用mcp server端暴露的工具方法
         * @param msg
         * @return
         */
        @GetMapping(&quot;/mcpclient/chat2&quot;)
        public Flux&lt;String&gt; chat2(@RequestParam(name=&quot;msg&quot;,defaultValue = &quot;北京&quot;) String msg) {
            return qwenChatModel.stream(msg);
        }
    
    }
    

通过MCP调用百度地图

  1. pom依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
    
  2. 配置文件

    server.port=8015
    # ?????????
    server.servlet.encoding.charset=UTF-8
    server.servlet.encoding.enabled=true
    server.servlet.encoding.force=true
    
    spring.application.name=saa16-ClientCallBaiduMapMcpServer
    
    #springai ????config
    spring.ai.dashscope.api-key=${aliQwen-apikey}
    
    # mcp client configuration
    spring.ai.mcp.client.request-timeout=60s
    spring.ai.mcp.client.toolcallback.enabled=true
    # json5 ???????????json????
    spring.ai.mcp.client.stdio.servers-configuration=classpath:mcp-server.json5
    

    mcp-server.json5文件内容如下:

    {
      "mcpServers": {
        "baidu-map": {
          "command": "cmd",
          "args": [
            "/c",
            "npx",
            "-y",
            "@baidumap/mcp-server-baidu-map"
          ],
          "env": {
            "BAIDU_MAP_API_KEY": "1ZGpFxxONtxxzhq3WEd5Z3BnNoB6"
          }
        }
      }
    }
    
  3. 配置类

    @Configuration
    public class SaaLLMConfig {
    
        private final String QWEN_MODEL="qwen-max";
    
        // 方式一
        @Value("${spring.ai.dashscope.api-key}")
        private String apiKey;
    
    
        @Bean(name = "qwenChatModel")
        public ChatModel qwenChatModel() {
            return DashScopeChatModel.builder()
                    // 配置apikey
                    .dashScopeApi(DashScopeApi.builder().apiKey(apiKey).build())
                    // 配置模型
                    .defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build())
                    .build();
        }
    
    
        @Bean("qwenChatClient")
        public ChatClient qwenChatClient(@Qualifier("qwenChatModel") ChatModel qwenChatModel, ToolCallbackProvider toolCallbackProvider) {
            return ChatClient.builder(qwenChatModel)
                    .defaultToolCallbacks(toolCallbackProvider.getToolCallbacks()) // mcp server端暴露的工具方法
                    .build();
        }
    }
    
  4. 控制器类

    @RestController
    public class McpClientCallBaiduMcpController {
    
        @Resource(name = "qwenChatModel")
        private ChatModel qwenChatModel;
    
        @Resource(name = "qwenChatClient")
        private ChatClient qwenChatClient;
    
        /**
         * 使用mcp server端暴露的工具方法
         * 可以查询天气,IP归属地、路线规划
         * demo参数:查询北纬39.9042°,东经116.4074°的天气情况
         * demo参数:查询61.149.121.66归属地
         * demo参数:查询从北京到上海的路线规划
         * @param msg
         * @return
         */
        @GetMapping("/mcpclient/chat")
        public Flux<String> chat(@RequestParam(name="msg",defaultValue = "北京") String msg) {
            return qwenChatClient.prompt(msg).stream().content();
        }
    
    }
    
posted @ 2026-11-09 17:38  NE_STOP  阅读(16)  评论(1)    收藏  举报