【AI Agent 全栈开发】MCP

🚀 欢迎来到我的CSDN博客:Optimistic _ chen

一名热爱技术与分享的全栈开发者,在这里记录成长,专注分享编程技术与实战经验,助力你的技术成长之路,与你共同进步!


🚀我的专栏推荐

专栏 内容特色 适合人群
🔥C语言从入门到精通 系统讲解基础语法、指针、内存管理、项目实战 零基础新手、考研党、复习
🔥Java基础语法 系统解释了基础语法、类与对象、继承 Java初学者
🔥Java核心技术 面向对象、集合框架、多线程、网络编程、新特性解析 有一定语法基础的开发者
🔥Java EE 进阶实战 Servlet、JSP、SpringBoot、MyBatis、项目案例拆解 想快速入门Java Web开发的同学
🔥Java数据结构与算法 图解数据结构、LeetCode刷题解析、大厂面试算法题 面试备战、算法爱好者、计算机专业学生
🔥Redis系列 从数据类型到核心特性解析 项目必备

🚀我的承诺:

✅ 文章配套代码:每篇技术文章都提供完整的可运行代码示例

✅ 持续更新:专栏内容定期更新,紧跟技术趋势

✅ 答疑交流:欢迎在文章评论区留言讨论,我会及时回复(支持互粉)


🚀 关注我,解锁更多技术干货!
⏳ 每天进步一点点,未来惊艳所有人!✍️ 持续更新中,记得⭐收藏关注⭐不迷路 ✨

📌 标签:#技术博客#编程学习#Java#C语言#算法#程序员

文章目录

  • 前言
  • 什么是MCP
  • [MCP Java SDK架构](#MCP Java SDK架构)
  • MCP使用
  • [自定义MCP Server](#自定义MCP Server)
    • [使用Stdio自定义MCP Server](#使用Stdio自定义MCP Server)
    • [使用SSE自定义MCP Server](#使用SSE自定义MCP Server)
  • 完结撒花!🎉

前言

当今快速发展的AI应用生态中,我们从优化提示词到使用Tool Calling突破大模型语言生成的界限,完成调用外部系统的实际任务。但是随着工具数量的增长和应用场景的复杂化,传统的Tool Calling实现方式暴露出一系列挑战

  • 工具接口格式不统一
  • 模型需硬编码工具定义
  • ...

为了解决这些问题,一个新的概念------MCP出现了。

什么是MCP

MCP(Model Context Protocol)模型上下文协议:事实上,MCP并不是代替Tool Calling的产物,而是为其提供一个统一、可扩展、跨平台的连接基础设施。

简单来说,MCP是Tool Calling的管理者。

  • Tool Calling:模型决定要不要调用工具,调哪个工具,
  • MCP:规定如何描述工具,如何发起调用,如何传递结果

有了MCP,AI只需要学习一种格式,MCP Server 负责翻译。

MCP Java SDK架构

MCP Java SDK提供MCP的Java实现,⽀持通过同步和异步通信模式与AI模型及⼯具进⾏标准化交互。

  • Client/Server分别表示请求的发起者和请求的接收者(顾客和后厨)
  • Session表示管理会话生命周期(订单管理系统)
  • Transport表示消息传递的方式(手机订餐+骑手送餐)

Transport传输协议

MCP Java SDK提供了多种"通信⽅式",让AI应⽤(客⼾端)能灵活地与各种⼯具服务(服务器)进⾏对话

目前支持三种传输方式:

  1. 基于Stdio 的进程间通信传输协议
    通过标准输⼊/输出(stdin和stdout)在两个程序之间传消息,⾼效直接
  2. 基于Java HttpClient的SSE客户端传输协议
    基于JavaHttpClient发起HTTP请求,并接收服务端持续发送的事件流
  3. WebFlux SSE客户端传输协议
    基于WebFlux的⾮阻塞、响应式编程模型,处理SSE流

MCP server

作为MCP协议的具体服务端实现,是对外提供特定功能的服务程序,我们可以直接去MCP Server社区去查找调用你想要的功能,社区中活跃着大量技术爱好者,致力于为MCP用户提供交流、资源共享等服务。

MCP Client

有了MCP 服务端后,如何在代码中调用这些服务,这就需要借助MCP 客户端。

作为MCP客户端是连接本地开发环境或应用与远程MCP服务器之间的桥梁,不同客户端对MCP协议的支持程度各不相同,⽤⼾可以根据⾃⼰的需求选择合适的客⼾端。

MCP使用

在使⽤MCPClient之前,通常需要先运⾏⼀个或多个MCPServer实例.⽽许多MCPServer是通过npx或uv命令启动的,在开始使⽤前,必须正确配置相应的运⾏环境。

准备一个MCP Server:可以到各大网站获取相应的配置

yaml 复制代码
{
    "mcpServers": {
        "baidu-map": {
            "command": "npx",
            "args": [
                "-y",
                "@baidumap/mcp-server-baidu-map"
            ],
            "env": {
                "BAIDU_MAP_API_KEY": "xxx"
            }
        }
    }
}

准备好MCP Server,这里通过Cursor来展示MCP的使用:

除了上述的客户端之外,我们也可以通过Spring AI代码来接入这些资源

首先,采用SpringAI-Alibaba来调用AI服务,引入依赖

xml 复制代码
<?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">
    <parent>
        <artifactId>spring-ai-project</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mcp-client-demo</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webflux</artifactId>
        </dependency>
        <!--        mcp client-->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-client</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud.ai</groupId>
                <artifactId>spring-ai-alibaba-bom</artifactId>
                <version>1.0.0.2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

配置文件application

yaml 复制代码
spring:
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY}
    mcp:
      client:
        request-timeout: 60000
        stdio:
          servers-configuration: classpath:/mcp/mcp-servers-config.json

同时引入百度地图的配置:mcp/mcp-servers-config.json

yaml 复制代码
{
  "mcpServers": {
    "baidu-map": {
      "command": "npx.cmd",
      "args": ["-y", "@baidumap/mcp-server-baidu-map"],
      "env": {
        "BAIDU_MAP_API_KEY": "nLpm876DutMXGl6gEwzJksuya0gbfk0B"
      }
    }
  }
}

在controller添加该工具

java 复制代码
@RestController
@RequestMapping("/chat")
public class ChatController {
    private ChatClient chatClient;

    public ChatController(DashScopeChatModel chatModel, ToolCallbackProvider toolCallbackProvider) {
        this.chatClient = ChatClient.builder(chatModel)
                .defaultToolCallbacks(toolCallbackProvider)
                .build();
    }
    @RequestMapping("/generate")
    public String generate(String message){
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }
}

ToolCallbackProvider 是 Spring AI 中用于向 AI 模型注册和提供可用工具(MCP Server 的工具)的核心接口。所以,之后只要有MCP配置进来,ToolCallbackProvider 就可以调用,完美解决了Tool Calling中各个工具接口不同的情况,实现动态工具组合。

自定义MCP Server

我们知道MCP有两种通信方式:Stdio 和 Streamable HTTP

前面提到的两个协议是Stdio 和 SSE,但是在 2025年3月26日的协议更新后,官方已用更强大的 Streamable HTTP 取代 SSE(解决了SSE连接容易断开的痛点)

controller/ChatController

java 复制代码
package com.zc.mcp.controller;

import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/chat")
public class ChatController {
    private ChatClient chatClient;

    public ChatController(DashScopeChatModel chatModel, ToolCallbackProvider toolCallbackProvider) {
        this.chatClient = ChatClient.builder(chatModel)
                .defaultToolCallbacks(toolCallbackProvider)
                .build();
    }
    @RequestMapping("/generate")
    public String generate(String message){
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }
}

application.yml

yaml 复制代码
spring:
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY}
    mcp:
      client:
        request-timeout: 60000
        stdio:
          servers-configuration: classpath:/mcp/mcp-servers-config.json

/mcp/mcp-servers-config.json

json 复制代码
{
  "mcpServers": {
    "stdio-server": {
      "command": "java",
      "args": [
        "-Dfile.encoding=UTF-8",
        "-Dspring.ai.mcp.server.stdio=true",
        "-Dlogging.pattern.console=",
        "-jar",
        "E:\\springAI\\mcp\\mcp-stdio-server\\target\\mcp-stdio-server-1.0-SNAPSHOT.jar"
      ]
    }
  }
}

使用Stdio自定义MCP Server

Service/WeatherService

java 复制代码
package com.zc.mcp.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
@Slf4j
public class WeatherService {

    private final Map<String, WeatherInfo> weatherCache = new ConcurrentHashMap<>();

    public WeatherService() {
        // 初始化天气数据
        weatherCache.put("北京", new WeatherInfo("晴转多云", "22~28", "南风2级", "78"));
        weatherCache.put("上海", new WeatherInfo("小雨", "18~24", "东风3级", "52"));
        weatherCache.put("广州", new WeatherInfo("晴朗", "25~33", "微风", "45"));
        weatherCache.put("深圳", new WeatherInfo("多云", "24~30", "东北风2级", "60"));
    }

    @Tool(description = "查询指定城市的天气信息,返回天气状况、温度范围、风力、空气质量")
    public String queryWeather(
            @ToolParam(description = "城市名称,如:北京、上海、广州、深圳") String city) {

        log.info("[HTTP MCP] queryWeather 调用,城市: {}", city);

        if (!weatherCache.containsKey(city)) {
            return String.format("未找到「%s」的天气信息,支持的城市:%s",
                    city, weatherCache.keySet());
        }

        WeatherInfo info = weatherCache.get(city);
        return String.format("""
            🌍 %s天气报告
            ─────────────────
            ☁️ 天气:%s
            🌡️ 温度:%s°C
            💨 风力:%s
            🏭 AQI:%s
            ─────────────────
            🕐 更新时间:%s
            """,
                city, info.weather, info.temp, info.wind, info.aqi,
                LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
        );
    }

    record WeatherInfo(String weather, String temp, String wind, String aqi) {}
}

config/ToolConfig

java 复制代码
package com.zc.mcp.config;

import com.zc.mcp.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 ToolConfig {
    @Bean
    public ToolCallbackProvider getWeather(WeatherService weatherService){
        return MethodToolCallbackProvider.builder()
                .toolObjects(weatherService)
                .build();
    }
}

application.yml

yaml 复制代码
spring:
  ai:
    mcp:
      server:
        name: stdio-server
        version: 0.0.1
  main:
    web-application-type: none
    banner-mode: off

生成jar包

使用SSE自定义MCP Server

注意:使用SSE传输策略时,依赖的主要选择 是取决于你是作为客户端还是服务端,以及是使用传统的同步SSE 还是WebFlux响应式SSE。

Starter 角色 被谁调用
spring-ai-starter-mcp-server MCP Server 本地MCP Client(通过Stdio)
spring-ai-starter-mcp-server-webmvc MCP Server 远程MCP Client(通过HTTP/SSE)
spring-ai-starter-mcp-server-webflux MCP Server 远程MCP Client(通过HTTP/SSE/WebFlux)
spring-ai-starter-mcp-client MCP Client Stdio/SSE客户端
spring-ai-starter-mcp-client-webflux MCP Client WebFlux客户端

在Web项目中添加spring-ai-starter-mcp-server webflux依赖会导致MCP端点(/sse)失效,这是因为项目中同时存在org.springframework.web.servlet.DispatcherServletorg.springframework.web.reactive.DispatcherHandler。根本原因是:当DispatcherServlet(Servlet)和DispatcherHandler(Reactive)共存时,Spring Boot会默认优先使用Servlet堆栈启动Web服务。这样应用将以传统的Tomcat(或Jetty/Undertow)服务器运行,而非WebFlux所需的Netty服务器,导致专属于WebFlux的/sse端点无法正常工作。

可以通过在配置文件中设置解决:

yaml 复制代码
server:
  port: 8081
spring:
  ai:
    mcp:
      server:
        name: sse-server
        version: 0.0.1
  main:
    web-application-type: reactive

SSE的pom.xml文件

xml 复制代码
<?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">
    <parent>
        <artifactId>mcp</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mcp-sse</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>
    <dependencies>
        <!-- Web 容器支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <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>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

service实现工具的业务逻辑,config将工具暴露给MCP Server.(代码和Stdio类似)

config 中的 MethodToolCallbackProvider 就像一个"扫描器",把 @Tool 方法收集起来"交"给MCP Server.

SSE定义MCP Server后,启动项目,在Cursor中配置好服务后,就能直接使用了。

yaml 复制代码
{
  "mcpServers": {
    "sse-server":{
      "url":"http://127.0.0.1:8081/sse"
    }
  }
}

想要在SpringAI中作为MCP Client调用该工具,注意controller的细节

java 复制代码
@RestController
@RequestMapping("/chat")
public class ChatController {

    private final ChatClient chatClient;

    @Autowired
    public ChatController(DashScopeChatModel chatModel,
                          SyncMcpToolCallbackProvider mcpToolProvider) {
        // DashScopeChatModel chatModel:阿里云通义千问的模型实例,负责实际调用大模型 API
        // SyncMcpToolCallbackProvider mcpToolProvider:MCP 工具提供者,负责将远程 MCP Server 的工具转换成 Spring AI 可用的工具
        this.chatClient = ChatClient.builder(chatModel)//告诉 ChatClient 使用哪个大模型
                .defaultToolCallbacks(mcpToolProvider.getToolCallbacks())  // 把从 MCP 工具提供者中获取所有远程工具设置为 ChatClient 的默认工具
                .build();
    }

    @RequestMapping("/generate")
    public String generate(String message) {
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }
}

完结撒花!🎉

如果这篇博客对你有帮助,不妨点个赞支持一下吧!👍
你的鼓励是我创作的最大动力~

想获取更多干货? 欢迎关注我的专栏 → optimistic_chen

📌 收藏本文,下次需要时不迷路!

我们下期再见!💫 持续更新中......


悄悄说:点击主页有更多精彩内容哦~ 😊

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/7471a32ba1fc4954b6ae528ef5b66475.png

相关推荐
charlie1145141911 小时前
嵌入式Linux嵌入式Linux驱动开发:板级DTS实操与完整实战演练——从修改设备树到点亮LED的完整闭环
linux·运维·驱动开发
listhi5201 小时前
Harris算子特征点提取、匹配和提纯的程序实现
人工智能·opencv·计算机视觉
AI袋鼠帝6 小时前
Codex终于进手机了!
人工智能
Lee川7 小时前
从零解剖一个 AI Agent Tool是如何实现的
前端·人工智能·后端
一个王同学7 小时前
从零到一 | CV转多模态大模型 | week09 | Minillava Refactor结合手搓和llava源码深入理解多模态大模型原理
人工智能·深度学习·机器学习·计算机视觉·改行学it
2601_957787587 小时前
全场景矩阵系统多端统一体验与跨端实时同步技术实践
大数据·人工智能·矩阵·多端统一·跨端同步
MAVER1CK7 小时前
Docker容器创建好后修改容器配置
运维·docker·容器
liudanzhengxi7 小时前
AI提示词极限赛:突破边界的艺术
人工智能
ZhengEnCi8 小时前
09-斯坦福CS336作业 📝
人工智能