Spring Ai Alibaba 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 中)无需重启代理应用

实战代码可见: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:中断!人类反馈介入,流程丝滑走完~

相关推荐
LeeAt20 小时前
手把手教你构建自己的MCP服务器并把它连接到你的Cursor
javascript·cursor·mcp
harykali20 小时前
Datawhale AI 夏令营:Task2从MCP入门到MCP Sever设计
算法·mcp
西柚配咖啡21 小时前
aichat-core简化 LLM 与 MCP 集成的前端核心库(TypeScript)
mcp
喜欢吃豆1 天前
深入企业内部的MCP知识(四):FastMCP装饰器与类方法:正确结合面向对象与MCP组件的实践指南
人工智能·python·大模型·mcp
亲爱的非洲野猪1 天前
Spring Cloud Gateway介绍 - -基础概念,简单工作原理和配置示例
java·spring boot·gateway
在努力的韩小豪1 天前
如何从0开始构建自己的第一个AI应用?(Prompt工程、Agent自定义、Tuning)
人工智能·python·llm·prompt·agent·ai应用·mcp
堆栈future2 天前
深度剖析Manus:如何打造低幻觉、高效率、安全可靠的Agentic AI系统
llm·aigc·mcp
GA琥珀2 天前
MCP是什么?(进阶版!)
mcp
老纪的技术唠嗑局2 天前
技术实操:基于 OceanBase 打造 MCP 顾问
mcp