用Spring AI接入MCP,实时查询天气情况

0 阅读7分钟

本文适合谁?  正在或计划将 AI 能力集成到 Java/Spring 服务中的技术人员。文章提供完整可运行代码,不讲概念空话,所有结论都有执行证据支撑。

MCP 是什么,为什么它很重要

MCP(Model Context Protocol)是 Anthropic 主导、多家厂商共同维护的开放协议,定义了 AI 模型与外部工具之间交互的标准方式。你可以把它理解为——给 AI 用的 USB 接口标准:只要工具实现了 MCP 协议,任何支持 MCP 的 AI 客户端都能直接调用,不需要重写适配代码。

一句话定义: MCP 让"AI 调用工具"这件事,从各家自定义的私有实现,变成了可互操作的行业标准——就像 HTTP 统一了 Web 请求一样。

传统方案 vs MCP 方案

❌ 传统做法

  • 工具注册写死在 AI 应用里
  • 新增工具必须重新部署 AI 服务
  • 不同框架各写一套适配
  • 工具崩了 AI 服务一起崩
  • 没有标准审计和追踪

✅ MCP 方案

  • 工具独立部署,按协议暴露
  • 动态发现,新工具无需重启
  • 语言无关,Java/Python 均可
  • 工具故障不影响 AI 宿主
  • 标准化 JSON-RPC 可审计

整体架构

本方案由两个独立的 Spring Boot 服务组成,职责完全分离:

🛠MCP Tool Server(:8080)工具提供方。通过 @Tool 注解声明业务工具,经由 Streamable HTTP 协议对外暴露。可独立部署、独立扩缩容,对 AI 宿主完全透明。💬AI Chat Service(:8081)面向用户的 AI 对话服务。内嵌 MCP 客户端,通过 ToolCallbackProvider 自动将服务端所有工具注入 ChatClient,业务代码无感知工具细节。Image▲ 图1:系统架构图 — 两服务分离,MCP 协议桥接,工具调用跨越网络边界3 

一次工具调用的完整流程

用户发送一条消息"ORD-1001 订单状态是什么",背后经过 8 个步骤:

1用户发送消息POST /api/chat,携带 sessionId 和消息文本。2ChatClient 组装上下文将当前消息 + 历史对话记录组装为 Prompt,附带可用工具列表(来自 MCP 服务端)。3LLM 决定调用工具模型分析 Prompt,判断需要调用 getOrderStatus 工具,生成带参数的工具调用指令。4MCP 客户端转发请求Spring AI 的 SyncMcpToolCallbackProvider 将工具调用封装为标准 MCP JSON-RPC 请求,通过 Streamable HTTP 发送到工具服务端。5工具方法执行MCP 服务端路由到 OrderTool.getOrderStatus(),查询订单数据,返回结构化 JSON 结果。6LLM 基于工具结果生成答案工具结果返回给 LLM,模型结合订单数据生成自然语言回答。7结果写入会话历史用户消息 + 助手回复写入 ConversationStore,下一轮对话可引用。8返回给用户JSON 响应包含回答文本、使用的模型、耗时毫秒数。Image▲ 图2:请求流程图 — 泳道清晰展示各层职责,第 4-5 步是 MCP 协议跨服务调用的核心4 

6 个 MCP 工具一览

本方案在 MCP 服务端注册了 6 个工具,覆盖订单、商品、天气三个领域。其中 天气工具直接调用真实 API(open-meteo.com),提供可独立验证的执行证据。

工具名称所属类功能说明
getOrderStatusOrderTool按订单 ID 查询状态、配送日期和商品明细
getOrderHistoryOrderTool查询某客户的全部历史订单列表
searchProductsProductTool按关键词 + 品类搜索产品目录
checkInventoryProductTool查询指定 SKU 的实时库存状态
getWeatherWeatherTool按经纬度获取当前天气(调用真实 API)
getCityWeatherWeatherTool按城市名获取当前天气(支持 10 个主要城市)

关键代码解析

① 用注解声明一个 MCP 工具

只需在 Spring Bean 的方法上加 @Tool 注解,Spring AI 自动将其注册为 MCP 工具,并根据方法签名生成 JSON Schema——零样板代码:

@Service
public class OrderTool {

    private final OrderRepository orderRepository;

    @McpTool(description = "根据订单 ID 获取订单当前状态和详情")
    public Map<String, Object> getOrderStatus(
            @McpToolParam(description = "唯一订单标识符,例如 ORD-12345") String orderId) {

        return orderRepository.findById(orderId)
                .map(order -> Map.of(
                        "orderId", order.getId(),
                        "status", order.getStatus(),
                        "estimatedDelivery", order.getEstimatedDelivery().toString(),
                        "items", order.getItems().size()
                ))
                .orElseThrow(() -> new IllegalArgumentException("订单不存在:" + orderId));
    }
}

② 客户端一行代码完成工具注入

AI 宿主只需在构建 ChatClient 时注入 ToolCallbackProvider,之后的工具发现、调用、结果回传全部自动完成——业务代码不感知任何工具细节:

@Configuration
public class ChatConfig {

    @Bean
    ChatClient chatClient(ChatModel chatModel,
                          McpSyncClientToolCallbackProvider toolCallbackProvider) {
        return ChatClient.builder(chatModel)
                .defaultTools(toolCallbackProvider)
                .build();
    }
}

③ 配置两个关键的 application.properties

# MCP 服务端:Streamable HTTP 模式
spring.ai.mcp.server.name=exesolution-tool-server 
spring.ai.mcp.server.protocol=STREAMABLE
server.port=8080
# STATELESS 模式适合无会话亲和性的水平扩展
# spring.ai.mcp.server.protocol=STATELESS
# MCP 客户端:自动发现服务端工具
spring.ai.mcp.client.toolcallback.enabled=true
spring.ai.mcp.client.connections.tool-server.url=http://mcp-tool-server:8080/mcp
spring.ai.mcp.client.connections.tool-server.transport=STREAMABLE_HTTP
# 模型配置(换成 Anthropic 只需改这两行)
spring.ai.openai.api-key=${OPENAI_API_KEY} 
spring.ai.openai.chat.options.model=gpt-4o-mini  

三分钟跑起来 + 验证

前置条件:Docker Desktop 已启动,OPENAI_API_KEY 已配置到 .env 文件。Shell 快速启动

  1. 启动两个服务docker compose up -d --build
  2. 等约 30 秒,检查健康状态curl -s http://localhost:8080/actuator/health | jq .status curl -s http://localhost:8081/actuator/health | jq .status 预期:两个都返回 "UP"
  3. 查看已注册的 MCP 工具(应有 6 个)curl -s -u admin:admin-secret \   http://localhost:8080/admin/tools | jq .toolCount
  4. 发一条消息,触发工具调用curl -s -u user:user-secret -X POST \   http://localhost:8081/api/chat \   -H "Content-Type: application/json" \   -d '{"sessionId":"test","message":"ORD-1001 订单状态?"}' \   | jq .reply

执行第 4 步后,后台 mcp-tool-server 的日志中会出现:

Log docker compose logs mcp-tool-server | grep "[MCP]"// 这条日志证明:LLM 决定调用工具 → MCP 协议跨服务传输 → 工具方法执行[MCP] getOrderStatus called: orderId=ORD-1001 [MCP] getOrderStatus: found status=SHIPPED delivery=2026-04-207 

最强验证:实时天气数据

WeatherTool 在运行时真实调用 open-meteo.com 的免费 API(无需 API Key),返回当前时刻的真实气温。这个数字会随时间变化,读者可以独立核实——这是最直接的"代码真实运行"证据。

curl -s -u user:user-secret -X POST \   http://localhost:8081/api/chat \   -H "Content-Type: application/json" \   -d '{"sessionId":"w1","message":"东京现在天气如何?"}' \   | jq .reply
# 典型输出(温度为真实实时数据):"东京当前气温 22.4°C(72.3°F),  天气状况:多云,风速 14.2 km/h。  数据来源:open-meteo.com(实时)"
# 同时可用原始 API 交叉验证:
curl -s "https://api.open-meteo.com/v1/forecast\ ?latitude=35.6762&longitude=139.6503\ ¤t=temperature_2m" | jq .current.temperature_2m

⚡ 为什么这个方案值得在生产中使用

  • 工具与 AI 宿主完全解耦,独立部署、独立版本,互不影响
  • 动态工具发现:新增工具后,AI 宿主下一次调用即可感知,无需重启
  • 语言无关的标准协议:MCP 服务端可以是 Python、Go、Node.js——客户端无感知
  • 同一套工具服务多个 AI 应用:Claude Desktop、VS Code Copilot、自建应用均可接入
  • Streamable HTTP 支持有状态会话 + 无状态水平扩展两种部署模式
  • 所有工具调用有完整日志,结合 OpenTelemetry 可实现端到端链路追踪

部分限制

会话历史存在内存中,服务重启后丢失。生产环境需替换为 Redis 或 PostgreSQL 支持的 ChatMemory/mcp 端点默认无认证,依赖内网隔离。生产环境应添加双向 TLS 或 API Key 头部校验。当前只连接一个 MCP 服务端。连接多个服务端需要注册多个 SyncMcpClient Bean 并组合 ToolCallbackProvider。Spring AI 1.1.x(带 @McpTool 注解的版本)目前仍是里程碑版本,生产使用前请关注 GA 发布节奏。9 

三个生产扩展方向

🔌接入 Claude Desktop / VS Code
同一套 MCP 服务端 JAR,启动时加 spring.ai.mcp.server.stdio=true,无需修改代码,即可直接被 Claude Desktop 通过 STDIO 协议调用。一套工具,多个 AI 客户端共享。🔄替换 LLM 提供商为 Claude
在 pom.xml 中将 spring-ai-starter-model-openai 换为 spring-ai-starter-model-anthropic,修改两行配置,所有业务代码不动。📊添加 OpenTelemetry 全链路追踪
在两个服务中引入 Micrometer OTel bridge,即可将"用户请求 → LLM 调用 → MCP 工具执行"的完整链路以单个 Trace 可视化,快速定位性能瓶颈。

更多可落地可执行的AI技术方案,请持续关注此公众号【可执行AI方案库】

Image

若需要可运行的工程源代码,可以免费注册exesolution.com 网站下载。