第六篇:Spring AI 实战:将 Java 业务接口封装成企业级 MCP Server

干货:基于Spring AI把Java接口封装成MCP Server 本文介绍了如何通过SpringAI将Java接口封装成MCPServer。主要内容包括: 添加SpringAI依赖(三种传输实现方式可选) 创建接口类和实现类定义工具方法 注册MCP方法到启动类 配置MCP服务参数 启动并验证服务 通过Trae工具进行功能测试 文章展示了完整的配置步骤和代码示例,实现了一个包含用户信息查询功能的MCPServer,并提供了验证截图。最后提示读者可关注公众号获取完整代码。 阅读详情

第六篇:Spring AI 实战:将 Java 业务接口封装成企业级 MCP Server

系列:《2026 可验证企业级 AI Agent 工程》
本文关键词:Spring AI、MCP Server、Java、ClickHouse、Tool Calling、企业级 Agent

前言

上一篇介绍了 MCP 的协议原理。本篇开始进入工程实战:

使用 Spring Boot 和 Spring AI,把现有能源平台的查询、分析和工单能力封装为 MCP Server。

最终向 Agent 提供以下能力:

queryDeviceEnergy       查询设备能耗
queryAlarmHistory       查询告警历史
calculateEnergyBaseline 计算能耗基线
createMaintenanceOrder  创建检修工单

整体架构如下:

用户

AI Agent

MCP Client

Spring AI MCP Server

能源领域服务

ClickHouse

达梦数据库

工单系统

本文示例采用 Spring AI 2.x 的注解式 MCP API。不同版本的依赖名称和配置可能存在差异,实际开发时应以项目使用的 Spring AI BOM 为准。


一、为什么不能直接把 Service 暴露给 Agent?

假设系统中已经存在以下业务方法:

public List<EnergyData> queryEnergy(
        String deviceId,
        LocalDateTime startTime,
        LocalDateTime endTime) {
    // 查询ClickHouse
}

最简单的做法,是直接把这个方法注册成工具。

但生产环境还需要考虑:

  • 用户是否有权查询该设备;
  • 时间范围是否合理;
  • 返回数据是否过大;
  • 是否包含敏感字段;
  • 调用失败后能否重试;
  • 查询是否需要限流;
  • 工具行为是否可审计;
  • 返回结构是否便于模型理解。

因此,推荐在业务服务外增加一层 MCP Adapter:

MCP Tool
  ↓ 参数校验、权限判断、结果裁剪
Application Service
  ↓ 业务逻辑
Repository / Remote Client
  ↓
数据库与外部系统

MCP 层负责协议适配,业务规则仍然由领域服务负责。


二、创建 MCP Server 模块

建议在多模块项目中单独创建 MCP Server:

platform-energy-parent
├── core
│   ├── energy-base
│   └── energy-domain
├── platform-energy-manage
├── platform-energy-task
└── platform-energy-mcp-server
    ├── src/main/java
    │   └── com/smalleel/mcp
    │       ├── tool
    │       ├── resource
    │       ├── service
    │       ├── security
    │       └── dto
    └── src/main/resources
        └── application.yml

单独划分模块有三个好处:

  1. 不需要把整个管理后台暴露给 Agent;
  2. 可以独立部署、扩容和限流;
  3. 可以单独管理工具权限与审计规则。

三、添加 Spring AI MCP 依赖

使用 WebMVC 和 Streamable HTTP:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>${spring-ai.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
    </dependency>

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

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

如果项目使用 WebFlux,可以改为:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>

WebMVC 更适合现有同步业务服务,WebFlux 更适合大量异步 I/O 场景。


四、配置 MCP Server

application.yml 示例:

server:
  port: 8090

spring:
  application:
    name: platform-energy-mcp-server

  ai:
    mcp:
      server:
        name: energy-mcp-server
        version: 1.0.0
        type: SYNC
        protocol: STREAMABLE
        annotation-scanner:
          enabled: true

常见传输模式:

模式配置适用场景
STDIOstdio=true本地工具、IDE 插件
SSEprotocol=SSE兼容旧版客户端
Streamable HTTPprotocol=STREAMABLEWeb 服务与远程调用
Stateless HTTPprotocol=STATELESS云原生和水平扩容

如果当前 Java SDK 尚未完整支持最新 MCP 规范,应优先选择客户端和服务端都支持的协议模式。


五、设计工具返回对象

模型更适合处理结构化结果,而不是数据库实体。

public record DeviceEnergyResult(
        String deviceId,
        String deviceName,
        String startTime,
        String endTime,
        BigDecimal totalEnergy,
        BigDecimal baselineEnergy,
        BigDecimal deviationRate,
        String unit,
        String dataQuality,
        List<AbnormalPeriod> abnormalPeriods
) {
}

异常时段:

public record AbnormalPeriod(
        String startTime,
        String endTime,
        BigDecimal averagePower,
        BigDecimal deviationRate
) {
}

返回对象应该做到:

  • 字段名称明确;
  • 数值包含单位;
  • 时间包含时区;
  • 明确数据质量;
  • 避免无关数据库字段;
  • 控制集合最大长度。

不推荐直接返回:

List<Map<String, Object>>

因为模型无法稳定理解每个字段的业务含义。


六、实现能源查询领域服务

MCP Tool 不应该直接编写 SQL。

@Service
public class DeviceEnergyService {

    private final EnergyRepository energyRepository;
    private final DeviceRepository deviceRepository;

    public DeviceEnergyService(
            EnergyRepository energyRepository,
            DeviceRepository deviceRepository) {
        this.energyRepository = energyRepository;
        this.deviceRepository = deviceRepository;
    }

    public DeviceEnergyResult queryDeviceEnergy(
            String deviceId,
            LocalDateTime startTime,
            LocalDateTime endTime,
            String granularity) {

        validateTimeRange(startTime, endTime);

        Device device = deviceRepository.findById(deviceId)
                .orElseThrow(() ->
                        new IllegalArgumentException("设备不存在:" + deviceId));

        EnergyStatistics statistics =
                energyRepository.aggregate(
                        deviceId,
                        startTime,
                        endTime,
                        granularity
                );

        BigDecimal baseline =
                energyRepository.calculateBaseline(
                        deviceId,
                        startTime,
                        endTime
                );

        BigDecimal deviationRate =
                calculateDeviation(statistics.totalEnergy(), baseline);

        return new DeviceEnergyResult(
                device.id(),
                device.name(),
                startTime.toString(),
                endTime.toString(),
                statistics.totalEnergy(),
                baseline,
                deviationRate,
                "kWh",
                statistics.dataQuality(),
                statistics.abnormalPeriods()
        );
    }

    private void validateTimeRange(
            LocalDateTime startTime,
            LocalDateTime endTime) {

        if (!endTime.isAfter(startTime)) {
            throw new IllegalArgumentException(
                    "结束时间必须晚于开始时间"
            );
        }

        if (Duration.between(startTime, endTime).toDays() > 31) {
            throw new IllegalArgumentException(
                    "单次查询时间范围不能超过31天"
            );
        }
    }

    private BigDecimal calculateDeviation(
            BigDecimal current,
            BigDecimal baseline) {

        if (baseline == null
                || baseline.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }

        return current.subtract(baseline)
                .divide(baseline, 4, RoundingMode.HALF_UP);
    }
}

这里将时间范围限制在 31 天,避免 Agent 一次查询数年的原始数据。


七、使用 @McpTool 暴露查询工具

@Component
public class EnergyMcpTools {

    private final DeviceEnergyService deviceEnergyService;

    public EnergyMcpTools(
            DeviceEnergyService deviceEnergyService) {
        this.deviceEnergyService = deviceEnergyService;
    }

    @McpTool(
            name = "queryDeviceEnergy",
            title = "查询设备能耗",
            description = """
                    查询指定设备在某个时间范围内的能耗统计和异常时段。

                    适用于:
                    1. 查询设备总能耗;
                    2. 对比历史能耗基线;
                    3. 定位能耗异常时段。

                    不适用于:
                    1. 查询设备告警;
                    2. 修改设备配置;
                    3. 查询超过31天的原始数据。
                    """,
            generateOutputSchema = true,
            annotations = @McpTool.McpAnnotations(
                    readOnlyHint = true,
                    destructiveHint = false,
                    idempotentHint = true,
                    openWorldHint = false
            )
    )
    public DeviceEnergyResult queryDeviceEnergy(
            @McpToolParam(
                    description = "设备唯一编号,例如AC-001",
                    required = true)
            String deviceId,

            @McpToolParam(
                    description = "ISO-8601格式开始时间",
                    required = true)
            String startTime,

            @McpToolParam(
                    description = "ISO-8601格式结束时间",
                    required = true)
            String endTime,

            @McpToolParam(
                    description = "聚合粒度:minute、hour或day",
                    required = true)
            String granularity) {

        return deviceEnergyService.queryDeviceEnergy(
                deviceId,
                LocalDateTime.parse(startTime),
                LocalDateTime.parse(endTime),
                validateGranularity(granularity)
        );
    }

    private String validateGranularity(String granularity) {
        Set<String> allowed =
                Set.of("minute", "hour", "day");

        if (!allowed.contains(granularity)) {
            throw new IllegalArgumentException(
                    "granularity只能是minute、hour或day"
            );
        }

        return granularity;
    }
}

generateOutputSchema = true 会为非基础类型返回值生成输出 Schema,帮助客户端和模型理解返回结构。

工具注解中的 Hint 需要正确填写:

readOnlyHint      是否只读
destructiveHint   是否可能产生破坏性修改
idempotentHint    重复调用是否具有相同结果
openWorldHint     是否会访问开放外部环境

但这些只是提示,不能代替服务端安全控制。


八、工具描述为什么如此重要?

下面这种描述几乎没有价值:

@McpTool(description = "查询数据")

模型无法知道:

  • 查询什么数据;
  • 什么时候应该调用;
  • 参数如何填写;
  • 与其他工具有什么区别。

一份高质量描述应包含:

工具做什么
适合哪些问题
不适合哪些问题
参数格式
返回内容
边界条件

工具描述越明确,Agent 越不容易:

  • 选错工具;
  • 重复调用;
  • 构造错误参数;
  • 在信息不足时盲目调用。

九、实现告警历史查询工具

public record AlarmResult(
        String alarmId,
        String deviceId,
        String alarmType,
        String alarmLevel,
        String alarmTime,
        String recoveryTime,
        String status
) {
}
@Component
public class AlarmMcpTools {

    private final AlarmService alarmService;

    public AlarmMcpTools(AlarmService alarmService) {
        this.alarmService = alarmService;
    }

    @McpTool(
            name = "queryAlarmHistory",
            description = """
                    查询指定设备在给定时间范围内的告警记录。
                    最多返回100条,按告警时间倒序排列。
                    如果需要分析能耗,请使用queryDeviceEnergy。
                    """,
            generateOutputSchema = true,
            annotations = @McpTool.McpAnnotations(
                    readOnlyHint = true,
                    destructiveHint = false,
                    idempotentHint = true,
                    openWorldHint = false
            )
    )
    public List<AlarmResult> queryAlarmHistory(
            @McpToolParam(
                    description = "设备唯一编号",
                    required = true)
            String deviceId,

            @McpToolParam(
                    description = "ISO-8601格式开始时间",
                    required = true)
            String startTime,

            @McpToolParam(
                    description = "ISO-8601格式结束时间",
                    required = true)
            String endTime) {

        return alarmService.query(
                deviceId,
                LocalDateTime.parse(startTime),
                LocalDateTime.parse(endTime),
                100
        );
    }
}

必须在服务端设置返回数量上限,不能允许模型自行决定无限返回。


十、使用 @McpResource 暴露只读资源

设备档案更适合作为 Resource,而不是 Tool:

@Component
public class DeviceResources {

    private final DeviceService deviceService;
    private final ObjectMapper objectMapper;

    public DeviceResources(
            DeviceService deviceService,
            ObjectMapper objectMapper) {
        this.deviceService = deviceService;
        this.objectMapper = objectMapper;
    }

    @McpResource(
            uri = "energy://device/{deviceId}/profile",
            name = "device-profile",
            title = "设备档案",
            description = "读取指定设备的基础档案和技术参数",
            mimeType = "application/json"
    )
    public String getDeviceProfile(String deviceId)
            throws JsonProcessingException {

        DeviceProfile profile =
                deviceService.getSafeProfile(deviceId);

        return objectMapper.writeValueAsString(profile);
    }
}

注意,getSafeProfile() 应提前移除:

  • 设备控制密码;
  • 内部网络地址;
  • 数据源账号;
  • 不允许当前用户查看的字段。

十一、使用 @McpPrompt 封装分析模板

可以将企业分析流程封装为 MCP Prompt:

@Component
public class EnergyPrompts {

    @McpPrompt(
            name = "analyze-energy-anomaly",
            title = "分析设备能耗异常",
            description = "根据能耗、告警和历史基线分析异常原因"
    )
    public GetPromptResult analyzeEnergyAnomaly(
            @McpArg(
                    name = "deviceId",
                    description = "设备唯一编号",
                    required = true)
            String deviceId,

            @McpArg(
                    name = "date",
                    description = "需要分析的日期",
                    required = true)
            String date) {

        String message = """
                请分析设备 %s 在 %s 的能耗异常。

                执行步骤:
                1. 使用queryDeviceEnergy查询能耗及历史基线;
                2. 使用queryAlarmHistory查询同期告警;
                3. 将事实、推断和待确认事项分开;
                4. 每个结论必须说明数据依据;
                5. 数据不足时不得虚构原因。
                """.formatted(deviceId, date);

        return GetPromptResult.builder(
                List.of(new PromptMessage(
                        Role.USER,
                        TextContent.builder(message).build()
                ))
        ).description("设备能耗异常分析流程").build();
    }
}

Prompt 适合封装业务分析流程,但安全规则仍然应该放在代码和权限系统中。


十二、如何处理创建工单这类写操作?

创建工单不是只读操作,必须更加谨慎。

public record CreateOrderResult(
        String orderId,
        String status,
        boolean created,
        String message
) {
}
@Component
public class MaintenanceOrderTools {

    private final MaintenanceOrderService orderService;

    public MaintenanceOrderTools(
            MaintenanceOrderService orderService) {
        this.orderService = orderService;
    }

    @McpTool(
            name = "createMaintenanceOrder",
            description = """
                    为指定设备创建检修工单。

                    调用前必须确认:
                    1. 用户已经明确同意创建;
                    2. deviceId和故障描述已经确认;
                    3. idempotencyKey已经生成。

                    不得根据未经验证的模型推断自动创建工单。
                    """,
            generateOutputSchema = true,
            annotations = @McpTool.McpAnnotations(
                    readOnlyHint = false,
                    destructiveHint = false,
                    idempotentHint = true,
                    openWorldHint = false
            )
    )
    public CreateOrderResult createMaintenanceOrder(
            @McpToolParam(
                    description = "设备唯一编号",
                    required = true)
            String deviceId,

            @McpToolParam(
                    description = "经过确认的故障描述",
                    required = true)
            String faultDescription,

            @McpToolParam(
                    description = "用户确认标识,必须为true",
                    required = true)
            boolean confirmed,

            @McpToolParam(
                    description = "幂等键,相同业务请求必须使用相同值",
                    required = true)
            String idempotencyKey) {

        if (!confirmed) {
            throw new IllegalArgumentException(
                    "创建工单前必须获得用户确认"
            );
        }

        return orderService.create(
                deviceId,
                faultDescription,
                idempotencyKey
        );
    }
}

需要强调的是:

confirmed=true 不能作为唯一的安全证明。

因为该值可能由模型自行生成。正式系统还应该由 Host 或审批系统签发不可伪造的确认凭证,并由 MCP Server 验证:

confirmationToken
userId
operation
resourceId
expiredAt
signature

十三、加入身份认证和权限控制

工具被发现,不代表调用者有权执行。

可以使用 Spring Security 将 MCP Server 配置为 OAuth2 Resource Server:

@Configuration
@EnableWebSecurity
public class McpSecurityConfiguration {

    @Bean
    SecurityFilterChain securityFilterChain(
            HttpSecurity http) throws Exception {

        return http
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/actuator/health")
                        .permitAll()
                        .requestMatchers("/mcp/**")
                        .authenticated()
                        .anyRequest()
                        .denyAll()
                )
                .oauth2ResourceServer(oauth2 ->
                        oauth2.jwt(Customizer.withDefaults())
                )
                .build();
    }
}

配置 JWT 签发方:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth.example.com

除此之外,还需要执行资源级权限校验:

public void checkDevicePermission(
        String userId,
        String deviceId,
        String permission) {

    if (!permissionService.hasDevicePermission(
            userId,
            deviceId,
            permission)) {
        throw new AccessDeniedException(
                "无权访问设备:" + deviceId
        );
    }
}

只校验“用户已经登录”是不够的,还必须校验:

用户是否属于当前租户
用户是否有权访问当前设备
用户是否拥有工具对应权限
当前时间是否允许执行
操作是否需要二次审批

十四、不要让 Agent 直接传递 SQL

下面的工具设计存在严重风险:

@McpTool
public List<Map<String, Object>> executeSql(String sql) {
    return jdbcTemplate.queryForList(sql);
}

即使增加“只允许 SELECT”的 Prompt,也无法真正保证安全。

风险包括:

  • SQL 注入;
  • 越权查询;
  • 敏感表泄露;
  • 超大结果集;
  • 慢查询拖垮数据库;
  • 方言绕过;
  • Prompt Injection 诱导查询。

正确方式是暴露业务语义:

queryDeviceEnergy(
    deviceId,
    startTime,
    endTime,
    granularity
)

由服务端根据受控参数构造 SQL。


十五、工具异常应该如何返回?

根据 Spring AI MCP 注解约定,RuntimeException 可以转换为工具错误结果,模型能够读取错误并决定是否修正参数或重试。

例如:

if (!deviceExists(deviceId)) {
    throw new IllegalArgumentException(
            "设备不存在,请先确认deviceId"
    );
}

错误信息应满足:

  • 对模型具有可操作性;
  • 不暴露堆栈信息;
  • 不暴露 SQL;
  • 不暴露服务器路径;
  • 不包含密码或 Token。

不推荐:

NullPointerException at EnergyMapper.java:128
SQLState 42000
jdbc:clickhouse://192.168.x.x...

推荐:

{
  "errorCode": "DEVICE_NOT_FOUND",
  "message": "设备不存在,请先查询设备档案确认deviceId",
  "retryable": false
}

对于超时和临时网络异常,可以明确告诉 Agent 是否允许重试。


十六、增加幂等性控制

查询工具天然接近幂等,但写入工具必须主动设计。

例如创建工单:

@Transactional
public CreateOrderResult create(
        String deviceId,
        String description,
        String idempotencyKey) {

    Optional<MaintenanceOrder> existing =
            orderRepository.findByIdempotencyKey(
                    idempotencyKey
            );

    if (existing.isPresent()) {
        return toResult(existing.get(), false);
    }

    MaintenanceOrder order =
            orderRepository.save(
                    MaintenanceOrder.create(
                            deviceId,
                            description,
                            idempotencyKey
                    )
            );

    return toResult(order, true);
}

Agent 可能因为以下原因重复调用:

  • 模型误判;
  • 请求超时;
  • 客户端重试;
  • 网络中断;
  • 任务恢复。

因此,写操作不能假设只会执行一次。


十七、添加审计切面

可以通过注解和 AOP 统一记录工具调用。

@Aspect
@Component
public class McpToolAuditAspect {

    private static final Logger log =
            LoggerFactory.getLogger(McpToolAuditAspect.class);

    @Around("@annotation(mcpTool)")
    public Object audit(
            ProceedingJoinPoint point,
            McpTool mcpTool) throws Throwable {

        long start = System.currentTimeMillis();

        try {
            Object result = point.proceed();

            log.info(
                    "MCP tool success, tool={}, cost={}ms",
                    mcpTool.name(),
                    System.currentTimeMillis() - start
            );

            return result;
        } catch (Exception exception) {
            log.warn(
                    "MCP tool failed, tool={}, cost={}ms, type={}",
                    mcpTool.name(),
                    System.currentTimeMillis() - start,
                    exception.getClass().getSimpleName()
            );

            throw exception;
        }
    }
}

正式审计日志至少应包含:

TraceId
用户ID
租户ID
Agent标识
MCP Client标识
工具名称
脱敏后的参数摘要
权限判断结果
执行状态
耗时
结果数量
错误码

不要把密码、Token 和完整敏感数据写入日志。


十八、使用 MCP Client 验证工具

创建测试客户端时添加:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>

配置远程连接:

spring:
  ai:
    mcp:
      client:
        streamable-http:
          connections:
            energy-server:
              url: http://localhost:8090

将 MCP 工具交给 ChatClient:

@Bean
CommandLineRunner testEnergyAgent(
        ChatClient.Builder builder,
        ToolCallbackProvider mcpTools) {

    return args -> {
        ChatClient chatClient = builder.build();

        String response = chatClient.prompt()
                .system("""
                        你是一名能源分析助手。
                        必须基于工具返回的数据回答。
                        数据不足时明确说明,不得虚构。
                        """)
                .user("""
                      查询设备AC-001昨天的能耗,
                      与历史基线比较并指出异常时段。
                      """)
                .tools(mcpTools)
                .call()
                .content();

        System.out.println(response);
    };
}

此时执行过程大致为:

用户提出问题
→ 模型选择queryDeviceEnergy
→ MCP Client发送tools/call
→ MCP Server执行Java服务
→ 返回结构化能耗结果
→ 模型根据结果生成答案

十九、MCP Server 应该测试什么?

仅测试 Java 方法能够返回结果是不够的。

1. Schema 测试

检查:

  • 工具名称是否唯一;
  • 参数是否完整;
  • 必填字段是否正确;
  • 描述是否清晰;
  • 输出 Schema 是否稳定。

2. 参数边界测试

设备编号为空
设备不存在
开始时间晚于结束时间
时间范围超过31天
粒度不合法
非法时区
超长文本

3. 权限测试

未登录用户
跨租户访问
无设备权限
普通用户调用写入工具
审批凭证过期
伪造确认信息

4. 幂等测试

同一个 idempotencyKey 连续创建两次工单,系统只能产生一条业务记录。

5. Agent 行为测试

还需要使用真实模型验证:

是否会选对工具?
是否会重复调用?
是否会虚构工具结果?
工具失败后是否会无限重试?
执行写操作前是否会请求确认?

二十、生产部署建议

生产级 MCP Server 建议加入以下保护:

API Gateway
├── 身份认证
├── 工具级授权
├── 请求限流
├── Body大小限制
├── 超时与熔断
├── 敏感参数脱敏
└── 审计日志

工具级限制示例:

工具超时最大频率是否确认
queryDeviceEnergy5秒100次/分钟否
queryAlarmHistory5秒100次/分钟否
calculateEnergyBaseline15秒20次/分钟否
createMaintenanceOrder10秒5次/分钟是
stopDevice禁止自动调用0强审批

还应配置:

  • 数据库查询超时;
  • 最大返回记录数;
  • 最大时间跨度;
  • 工具调用并发数;
  • Agent 单任务调用预算;
  • 重试次数上限;
  • 熔断和降级策略。

二十一、常见开发误区

1. 一个 Tool 承担所有业务

executeEnergyOperation

模型难以判断它具体做什么,也难以进行精细权限控制。

应按业务动作拆分工具。

2. 返回数据库原始数据

几万条时序记录直接进入模型上下文,会增加成本并降低推理质量。

应先完成聚合和异常检测。

3. 通过 Prompt 控制权限

“没有权限时不要调用”不是安全机制。

权限必须在 MCP Server 中校验。

4. 写操作没有幂等键

网络重试可能造成重复工单、重复通知甚至重复控制指令。

5. Tool 中重复实现业务逻辑

MCP Tool 应作为适配层调用现有领域服务,不能形成第二套业务逻辑。

6. 忽略协议版本

MCP 规范、MCP Java SDK 和 Spring AI 的版本不一定同步。客户端与服务端必须进行兼容性验证。


二十二、最终工程结构

platform-energy-mcp-server
├── tool
│   ├── EnergyMcpTools.java
│   ├── AlarmMcpTools.java
│   └── MaintenanceOrderTools.java
├── resource
│   └── DeviceResources.java
├── prompt
│   └── EnergyPrompts.java
├── service
│   ├── DeviceEnergyService.java
│   └── MaintenanceOrderService.java
├── security
│   ├── McpSecurityConfiguration.java
│   └── DevicePermissionService.java
├── audit
│   └── McpToolAuditAspect.java
└── dto
    ├── DeviceEnergyResult.java
    ├── AlarmResult.java
    └── CreateOrderResult.java

核心设计原则是:

MCP层负责协议适配
领域层负责业务规则
数据层负责数据访问
安全层负责身份与权限
审计层负责调用追踪

总结

使用 Spring AI 构建 MCP Server,最简单的部分是添加 @McpTool。

真正决定系统能否进入生产环境的是:

  • 工具边界是否清晰;
  • 参数是否受到约束;
  • 返回结果是否结构化;
  • 权限是否在服务端校验;
  • 写操作是否需要确认;
  • 是否具备幂等、限流和审计;
  • 是否与现有领域服务正确解耦。

企业级 MCP Server 不只是一个“让大模型调用 Java 方法”的接口,而是:

AI Agent 与企业核心业务系统之间的一层安全、标准、可治理的能力网关。

下一篇:

《AI Agent 如何自主规划任务:ReAct、Plan-and-Execute 与状态机实战》

参考资料:

一条龙开发指南:MCP AI Agent 理论+项目实战开发你的MCP Server MCP(Model Connection Protocol)是Anthropic于2024年推出的AI模型连接协议,旨在为AI模型提供开放、通用的标准化接口,类似于“USB-C”接口,使AI模型能够无缝连接各种数据源和工具。MCP通过标准化协议解决了AI系统集成中的碎片化问题,提升了开发效率和系统可靠性。开发者可以通过简单的配置,快速接入不同的MCP Server,实现AI模型的多样化应用。MCP支持多种协议,如Stdio、SSE等,并通过JSON-RPC格式进行数据传输。文章还以腾讯云代码助手为例,展示了 阅读详情

相关推荐

Spring Boot 集成 Spring AI:实现可被大模型调用的 MCP Server

本文介绍了如何利用Spring AI框架构建MCP Server,使大模型能够调用业务系统的接口。主要内容包括:1)MCP协议简介,分为MCP Server和Client两部分;2)环境配置要求,需Java 17+和Spring Boot 3.5.0+;3)实现步骤,通过@McpTool注解将Service方法暴露给大模型调用;4)使用Cherry Studio作为MCP Client测试效果,成功实现订单查询和下单功能。本文仅实现MCP Server部分,后续将介绍如何开发MCP Client。

iioSnail的博客 2126

java 使用 spring AI 实战MCP

全称 Model Context Protocol 是一种专为人工智能模型设计的通信协议,于2024年11月由Anthropic推出的开放标准。它旨在解决复杂AI系统中多个模型或组件之间的协同、状态管理和资源优化问题,以及AI应用与外部数据源和工具集成中的挑战。MCP 是一个开放协议,用于标准化应用程序向大语言模型提供上下文的方式。

YXWik的博客 5793

PBR:双向反射分布函数(BRDF)介绍与Cook-Torrance模型的实现

PBR:双向反射分布函数(BRDF)介绍与Cook-Torrance模型的实现 BRDF简介 再介绍BRDF之前我们要引入渲染方程这个东西: 其中L表示辐射率,其公式为: 它表示了一个拥有辐射强度Φ的光源在单位面积A,单位立体角ω上的辐射出的总能量。 辐射率是辐射度量学上表示一个区域平面上光线总量的物理量,它受到入射光线与平面法线间的夹角θ的余弦值cosθ的影响:当直接...

小龙虾的博客 1万+

Spring AI系列——开发MCP Server和MCP Client(SSE方式)

本文介绍了如何创建和使用MCP Server的Java工程。首先通过Spring Initializr生成基础工程,添加MCP Server依赖并修改pom.xml文件。然后定义包含加减法工具的MathTool类,注入Spring容器并配置YAML文件。成功启动服务后会显示注册的工具信息。最后演示了两种使用方式:通过Chatbox设置AI模型提供方并配置MCP服务器,可以测试工具功能;或直接通过API调用。文章提供了完整的代码示例和配置步骤,帮助开发者快速搭建MCP Server服务。

运维@小兵的博客 8370

ANSYS Maxwell:创建即用型 3D 轴向磁通电机模型

Magnet Length” 是磁体的径向长度。“Magnet Thickness” 是磁体的轴向长度。为了对 AFM 进行更详细的分析,用户需要在 ANSYS Maxwell 中运行 3D FEA 分析。对于 2D 模型,它们类似于 MotorCAD 中的模型,后者对等效的直线电机进行建模。单击“确定”后,将创建一个 3D Maxwell 模型。“长度”是转子铁的轴向长度。然后需要定义定子的“磁芯”尺寸和材料。“长度”是定子铁芯的轴向长度。“转子”# 的极数必须与“定子”中定义的磁极相同。

jkh920184196的博客 1154

陀螺仪及四元数解算

陀螺仪 四元数

zb313982521的博客 2004

Spring AI开发MCP Server和MCP Client

本文介绍了基于SpringAI框架实现MCP(模型上下文协议)开发和调用的完整流程。首先开发了两个MCPServer:一个是通过STDIO协议发布的算术计算器服务,另一个是通过SSE协议发布的天气预报服务。然后实现了MCPClient客户端,集成阿里云Qwen大模型完成MCP调用。文章详细说明了MCP协议的概念、SpringAI的模块化架构,以及两种传输协议(STDIO和SSE)的特点和适用场景。通过具体代码示例展示了服务端和客户端的开发过程,包括工具注解、配置文件和测试验证。最后总结了SpringAI实现

9521

从零搭建《火影忍者》手游服务端:Linux环境部署与全流程实战

游戏服务器后端开发是构建大型多人在线体验的核心技术,其原理涉及高并发网络通信、分布式系统架构与数据一致性保障。在技术价值上,一套稳定高效的服务端不仅能支撑海量玩家实时交互,更是游戏业务逻辑、经济系统和安全防线的基石。典型的应用场景包括MMORPG、回合制卡牌、SLG等中重度手游。本文以《火影忍者》IP的横版卡牌回合制手游为例,聚焦于**手工服务端**的完整搭建过程,详细拆解从Linux系统初始化、MySQL与**Redis**缓存部署、核心服务编译、到代理后台联调的全链路实践,为开发者深入理解游戏后端架构提

weixin_30800615的博客 337

快速掌握MCP——Spring AI MCP包教包会

是一个开源的协议,可以让大模型应用更方便的集成各种数据源和工具,经典举例为:使用Type-C接口适配各种电器,使用统一的接口为大模型应用提供各种工具。MCP官网中将MCP主要分为:MCP Hosts、MCP Clients、MCP Servers、Local Data Sources、Remote Services这几个部分。同时,MCP官网还提供了多种语言的SDK,python、java、c#、typescript等都有。在本文中所使用到的框架为spring-ai。

水中加点糖 1万+

YOLOv8改进策略【卷积层】| CVPR-2023 部分卷积 PConv 轻量化卷积,降低内存占用

本文记录的是利用优化的目标检测方法研究。深度可分离卷积可以减少FLOPs,但会导致更高的内存访问,引起延迟并减慢整体计算。部分卷积利用逐点卷积处理通道冗余,减少模型计算量和内存访问量。Run, Don’t Walk: Chasing Higher FLOPS for Faster Neural Networks及其改进的实现代码如下: 四、创新模块 4.1 改进点⭐ 模块改进方法:直接加入(第五节讲解添加步骤)。模块加入如下:模块改进方法:基于的(第五节讲解添加步骤)。第二种改进方法是对中的进行改进,并将

Limiiiing的博客 1015

ROS 之 使用yocs_velocity_smoother对机器人速度进行限制

yocs_velocity_smoother是一个速度、加速度限制器,用来防止robot navigation的速度/转速过快,加速度/快减速过大。Bound incoming velocity messages according to robot velocity and acceleration limits. The velocity smoother nodelet runs together with the kobuki_node to apply robot’s velocity and .

https://shixuebin.gitee.io/hexo-blog/ 2622

聊聊spring ai的mcp server

本文主要研究一下spring ai的mcp server。

go4it 3557

SAP ABAP CO核心扩展-ZCKMLQS 多物料工厂的评估数量结构报表(CKMLQS)

CKMLQS 只能查看单一物料工厂的评估数量结构,SAP 没有提供多物料工厂的评估数量结构报表。这篇文章将实现 CO核心扩展-ZCKMLQS 多物料工厂的评估数量结构报表。

SAP李斌,本人承诺所有原创文章均不使用任何AI生成技术,请大家放心阅读。 1089

Spring AI 系列之八 - MCP Server

本章演示通过2种方式建立了MCP-Server服务,对于MCP的内容还比较多,这里只是一个入门。如果对这一块感兴趣的朋友,需要先研究一下MCP协议,里面涉及的配置也可以参考。

代码让AI扣 1906

Cadence OrCAD Capture软件启动过程卡死且无法恢复的解决方法

如下图所示Cadence OrCAD Capture软件在启动后出现卡死的现象,且等待后无法自动恢复,需要强行关闭,再次启动依然卡死。

fydar的博客 4043

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Spring AI MCP Server 技术详解与实战

选对传输方式CLI/桌面工具选 STDIO,Web 应用选 SSE(WebMVC/WebFlux),响应式优先 WebFlux。善用自动注册尽量通过 Bean 声明暴露 AI 能力,减少手工注册负担。充分利用通知机制日志、进度、资源变更、心跳等机制能极大提升服务可用性和可观测性。配置优先,代码最简多数功能通过配置即可实现,代码保持简洁。

IT技术学习与工作笔记分享 1332

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

从零开始用Java搭建Spring AI MCP Server:全面指南

本文介绍了使用Java搭建四种Spring AI MCP Server的全面指南。MCP(Model Context Protocol)是一套标准化协议,用于让服务器向AI应用暴露特定能力。文章详细讲解了基于Spring AI 1.1.0版本的四种主流MCP Server实现方式:STDIO(命令行工具)、SSE(实时数据流)、Streamable-HTTP(HTTP流式传输)和Stateless(无状态服务)。

2301_80017072的博客 3002

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

【Spring AI系列】10 分钟用 Spring AI 搭专属 MCP Server,手把手教程来了

Streamable HTTP 适合复杂网络交互,而 stdio 和 sse 应用更为高频,他俩一个主打本地交互的便捷,一个专注实时数据推送,这也是本文要重点讲解的内容,下面就来深入了解它们的原理与适用场景。2. 将打包好的jar包挪出来,我把jar包放到了E盘根目录,填写MCP Server配置文件后保存,下图中我的天气查询MCP Server就配置好了。4. 在xfc-mcp-server模块中编写天气查询服务,由于我本期教程只是打个样,就做了一个虚拟的天气查询服务,目的只是带领大家把流程走通。

lwb_0118的博客 1737

大模型开发 - 基于Spring AI 的 MCP Server 开发(中)

本文介绍了MCP协议及其在AI应用开发中的应用。MCP协议作为连接LLM与外部数据源的标准化接口,由Anthropic公司发起并得到行业支持。文章详细解析了MCP Server的角色与功能,包括提供资源、工具和提示三种核心能力,并探讨了基于Spring AI框架开发MCP应用的两种方式:构建MCP客户端或服务器。最后通过图书管理系统案例,展示了如何利用Spring AI的MCP功能实现智能图书查询和推荐服务。该方案将图书服务方法通过@Tool注解暴露为AI可调用的工具,实现了AI与业务系统的深度集成。

小工匠 2311

基于Spring AI开发本地Jenkins MCP Server服务

MCP是由开发了模型的,它是一个开放协议,它使 LLM 应用与外部数据源和工具之间的无缝集成成为可能。无论你是构建 AI 驱动的 IDE、改善chat 交互,还是构建自定义的 AI 工作流,MCP 提供了一种标准化的方式,将 LLM 与它们所需的上下文连接起来。

非专业Bug开发工程师的博客 1882

Java基于Spring-AI构建MCP Server以及MCP Client

server:port: 9999spring:ai:mcp:server:@Component@Tool(description = "可以获取到某个城市的天气信息")public Map<String, String> getWeather(@ToolParam(required = true,description = "入参城市,请传入城市拼音") String city) throws SQLException {

i_eat_lemon的博客 1100

Spring AI + MCP 实战:构建企业级 Agent 生态的基石

本文深入探讨 Spring AI 与 Anthropic 发布的 Model Context Protocol (MCP) 的深度集成。从 MCP 的协议原理出发,手把手教你如何通过 Java 生态构建高性能的 MCP Server 与 Client,打通企业私有数据与 AI 大模型之间的连接壁垒。

JavaAlenboy 878

Spring AI Alibaba 发布企业级 MCP 分布式部署方案

Spring AI Alibaba MCP 联合 Nacos,解决了企业级 AI Agent 的应用与落地场景中 MCP 分布式部署与动态更新的关键问题,其中包括流量的负载均衡、节点变更动态感知等关键解决方案。

阿里巴巴云原生的博客 2350

Java 开发玩转 MCP:从 Claude 自动化到 Spring AI Alibaba 生态整合

本文以原理与示例结合的形式讲解 Java 开发者如何基于 Spring AI Alibaba 框架玩转 MCP,主要包含以下内容。

阿里巴巴云原生的博客 1367

Spring AI MCP Server开发指南:企业级AI服务整合实践

在微服务架构中,协议优化与AI能力整合是提升系统性能的关键技术。MCP(Modular Control Protocol)作为一种高效的二进制通信协议,相比传统REST/GRPC具有更好的数据压缩率和流式支持,特别适合AI场景下的高吞吐需求。Spring生态通过Spring AI模块为开发者提供了便捷的AI服务集成方案,结合MCP协议可以实现低延迟、高并发的智能服务。这种技术组合在金融风控、智能客服等实时决策场景中表现优异,能有效解决传统规则引擎灵活性不足的问题。通过Spring AI MCP Server

weixin_33933118的博客 497
上一篇: 第五篇:MCP 深度解析:AI Agent 如何标准化连接数据库、API 与企业工具
下一篇: 第七篇:AI Agent 如何自主规划任务?ReAct、Plan-and-Execute 与状态机实战
yychen_java
博客等级 码龄8年 1451粉丝 77原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值