Spring AI Alibaba MCP Gateway:零代码实现实现存量应用转换 MCP

作者简介:你好,我是影子,Spring Ai Alibaba开源社区 Committer,持续分享Spring Ai Alibaba最新进展 + 业界各类AI工程相关的方案

  • 最近有断时间没更了,熟悉我的朋友知道我刚结束完毕业旅行,最近也因为入职,参与公司为期一周的文化相关的培训(培训的东西真是太赞了,每一条都直击高效生活的本质,受益非浅,主动积极、双赢思维,个人成长->公众成长等等),实在抽不出时间来写文章了,这不趁着周末赶快写点东西

本文是官网更详细的上手例子:Spring AI Alibaba MCP Gateway 正式发布,零代码实现存量应用转换 MCP 工具!

Gateway 实现存量应用转 MCP 工具

!TIP\] Spring AI Alibaba MCP Gateway 基于 Nacos 提供的 MCP server registry 实现,为普通应用建立一个中间代理层 Java MCP 应用。一方面将 Nacos 中注册的服务信息转换成 MCP 协议的服务器信息,以便 MCP 客户端可以无缝调用这些服务;另一方面可以实现协议转化,将 MCP 协议转换为对后端 HTTP、Dubbo 等服务的调用。基于 Spring AI Alibaba MCP Gateway,您无需对原有业务代码进行改造,新增或者删除 MCP 服务(在 Nacos 中)无需重启代理应用 实战代码可见:[github.com/GTyingzi/sp...](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2FGTyingzi%2Fspring-ai-tutorial "https://github.com/GTyingzi/spring-ai-tutorial") 下的 other 目录下的 nacos-restful 模块、mcp 目录下的 server 目录下的 mcp-gateway 模块

restful 服务

pom 文件

xml 复制代码
<properties>
    <nacos.version>3.0.2</nacos.version>
</properties>

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

    <dependency>
        <groupId>com.alibaba.nacos</groupId>
        <artifactId>nacos-client</artifactId>
        <version>${nacos.version}</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

application.yml

java 复制代码
server:
  port: ${SERVERPORT:18081}  # 默认端口为18081,可以通过命令行参数SERVERPORT覆盖

spring:
  application:
    name: nacos-restfult

  cloud:
    # nacos注册中心配置
    nacos:
      discovery:
        username: nacos
        password: nacos
        server-addr: 127.0.0.1:8848
        namespace: 4ad3108b-4d44-43d0-9634-3c1ac4850c8c # nacos3.*版本

config

java 复制代码
package com.spring.ai.tutorial.other.config;

import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;

/**
 * @author yingzi
 * @since 2025/7/7
 */
@Configuration
public class NacosConfig {

    private static final Logger logger = LoggerFactory.getLogger(NacosConfig.class);

    @Value("${spring.application.name}")
    private String serviceName;
    @Value("${spring.cloud.nacos.discovery.server-addr}")
    private String serverAddr;
    @Value("${spring.cloud.nacos.discovery.namespace}")
    private String namespace;
    @Value("${spring.cloud.nacos.discovery.username}")
    private String username;
    @Value("${spring.cloud.nacos.discovery.password}")
    private String password;

    @Value("${server.port}")
    private int port;

    @Bean
    public NamingService namingService() throws NacosException, UnknownHostException {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.NAMESPACE, Objects.toString(this.namespace, ""));
        properties.put(PropertyKeyConst.SERVERADDR, Objects.toString(this.serverAddr, ""));
        properties.put(PropertyKeyConst.USERNAME, Objects.toString(this.username, ""));
        properties.put(PropertyKeyConst.PASSWORD, Objects.toString(this.password, ""));

        NamingService namingService = NamingFactory.createNamingService(properties);
        init(namingService);
        return namingService;
    }

    private void init(NamingService namingService) throws NacosException, UnknownHostException {
        Instance instance = new Instance();
        // 自动获取本机IP
        instance.setIp(InetAddress.getLocalHost().getHostAddress());
        instance.setPort(port);
        instance.setMetadata(Map.of("register.timestamp", String.valueOf(System.currentTimeMillis())));
        logger.info("注册实例: {}:{}", instance.getIp(), port);
        namingService.registerInstance(serviceName, instance);
    }

}

Controller

java 复制代码
package com.spring.ai.tutorial.other.controller;

import com.spring.ai.tutorial.other.utils.ZoneUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author yingzi
 * @date 2025/4/6:12:56
 */
@RestController
@RequestMapping("/time")
public class TimeController {

    private static final Logger logger = LoggerFactory.getLogger(TimeController.class);

    /**
     * 获取指定时区的时间
     */
    @GetMapping("/city")
    public String getCiteTimeMethod(
            @RequestParam("timeZoneId") String timeZoneId) {

        logger.info("The current time zone is {}", timeZoneId);
        return String.format("The current time zone is %s and the current time is " + "%s", timeZoneId,
                ZoneUtils.getTimeByZoneId(timeZoneId));
    }
}
java 复制代码
package com.spring.ai.tutorial.other.utils;

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @author yingzi
 * @date 2025/3/25:14:59
 */
public class ZoneUtils {

    public static String getTimeByZoneId(String zoneId) {

        // Get the time zone using ZoneId
        ZoneId zid = ZoneId.of(zoneId);

        // Get the current time in this time zone
        ZonedDateTime zonedDateTime = ZonedDateTime.now(zid);

        // Defining a formatter
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");

        // Format ZonedDateTime as a string
        String formattedDateTime = zonedDateTime.format(formatter);

        return formattedDateTime;
    }

    public static void main(String[] args) {
        System.out.println(getTimeByZoneId("Asia/Shanghai"));
    }

}

效果:注册至 Nacos 中

nacos 配置

Mcp server 连接 restful 配置

在 MCP 管理-MCP 列表处创建 MCP Server

  • 填写命名空间:4ad3108b-4d44-43d0-9634-3c1ac4850c8c

,不填写默认为 Default

  • MCP 服务名:mcp-nacos-restful
  • 协议类型:这里选择 sse
  • HTTP 转 MCP 服务:这里选择 http
  • 后端服务:选择已有服务(因为我们的后端服务已经注册在 Nacos 中了,故直接使用即可,也可以通过新建服务指定 ip+port 的形式)
  • 描述:该项服务的说明
  • 服务版本:指定版本

新增 Tool 配置

Tool 名称:getCiteTimeMethod

Tool 描述:获取指定时区的时间

启用:True

Tool 入参描述:

  • timeZoneId;string;time zone id, such as Asia/Shanghai

协议转换配置(注意,这里输出的转换逻辑具体可看 com.alibaba.cloud.ai.mcp.nacos.gateway.jsontemplate 包下的 ResponseTemplateParser 类)

java 复制代码
{
"requestTemplate":{
      "method":"GET",
      "url":"/time/city",
      "argsToUrlParam":true
},
"responseTemplate":{
      "body":"{{.}}"
}
}

发布后,可到配置管理处看到相关配置信息

gateway 转接

pom 文件

java 复制代码
<properties>
    <!-- Spring AI Alibaba -->
    <spring-ai-alibaba.version>1.0.0.3-SNAPSHOT</spring-ai-alibaba.version>
</properties>

<dependencies>
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter-nacos-mcp-gateway</artifactId>
        <version>${spring-ai-alibaba.version}</version>
    </dependency>

    <!-- MCP Server WebFlux 支持(也可换成 WebMvc) -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
    </dependency>

</dependencies>

application.yml

yaml 复制代码
server:
  port: 19000

spring:
  application:
    name: mcp-gateway-server
  ai:
    mcp:
      server:
        name: mcp-gateway-server
        version: 1.0.0

    alibaba:
      mcp:
        nacos:
          namespace: 4ad3108b-4d44-43d0-9634-3c1ac4850c8c
          server-addr: 127.0.0.1:8848
          username: nacos
          password: nacos
          gateway:
            enabled: true
            service-names: mcp-nacos-restful # 和nacos配置的mcp server保持一致

# 调试日志
logging:
  level:
    io:
      modelcontextprotocol:
        client: DEBUG
        spec: DEBUG
        server: DEBUG

验证

启动对应的两个模块

  • nacos-restful 模块
  • mcp-gateway 模块

在利用 MCP Inspector 进行测试

参考资料

Spring AI Alibaba MCP Gateway 正式发布,零代码实现存量应用转换 MCP 工具!

往期文章解读

第一章内容

SpringAI(GA)的chat:快速上手+自动注入源码解读

SpringAI(GA):ChatClient调用链路解读

第二章内容

SpringAI的Advisor:快速上手+源码解读

SpringAI(GA):Sqlite、Mysql、Redis消息存储快速上手

第三章内容

SpringAI(GA):Tool工具整合---快速上手

SpringAI(GA):Tool源码+工具触发链路解读

第四章内容

SpringAI(GA):结构化输出的快速上手+源码解读

第五章内容

SpringAI(GA):内存、Redis、ES的向量数据库存储---快速上手

SpringAI(GA):向量数据库理论源码解读+Redis、Es接入源码

第六章内容

SpringAI(GA):RAG快速上手+模块化解读

SpringAI(GA):RAG下的ETL快速上手

SpringAI(GA):RAG下的ETL源码解读

第七章内容

SpringAI(GA):Nacos2下的分布式MCP

SpringAI(GA):Nacos3下的分布式MCP

SpringAI(GA):MCP源码解读

SpringAI(GA): SpringAI下的MCP源码解读

进阶:MCP服务鉴权案例

第八章内容

SpringAI(GA): 多模型评估篇

第九章内容

SpringAI(GA):观测篇快速上手+源码解读

第十章内容

Spring AI Alibaba Graph:快速入门

Spring AI Alibaba Graph:多节点并行---快速上手

Spring AI Alibaba Graph:节点流式透传案例

Spring AI Alibaba Graph:分配MCP到指定节点

Spring AI Alibaba Graph:中断!人类反馈介入,流程丝滑走完~

相关推荐
2013编程爱好者几秒前
【AI】豆包+千问下载以及使用指南
人工智能·千问·豆包
山科智能信息处理实验室1 分钟前
(ITES 2025)教育推荐系统综述:主流技术、应用场景与未来趋势
人工智能
OneThingAI5 分钟前
网心技术 | Claude Managed Agents 让 Harness 变成服务
人工智能·claude·onethingai·网心科技
不会编程的-程序猿10 分钟前
深度神经网络中不同激活函数、不同归一化的区别与使用
人工智能·神经网络·dnn
常宇杏起10 分钟前
AI安全进阶:AI系统日志审计与安全监控技巧
大数据·人工智能·安全
用户67570498850210 分钟前
AI开发实战2、只有 1% 的人知道!这样给 AI 发指令,写出的前端项目堪比阿里 P7
后端·aigc·ai编程
2501_9481142412 分钟前
星链4SAPI中转枢纽深度技术解构:架构优势、工程实践与演进脉络
大数据·人工智能·ai·架构
财经资讯数据_灵砚智能12 分钟前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年4月21日
人工智能·信息可视化·自然语言处理
KD12 分钟前
「OpenClaw」我写了个桌面控制Skill,让龙虾接管电脑!(MacOS版)
人工智能·开源·github
jay神12 分钟前
鸟类识别数据集 - CUB_200
人工智能·深度学习·目标检测·计算机视觉·目标跟踪·毕业设计