欢迎光临
我们一直在努力

206-Spring AI Alibaba MCP 股票查询服务器示例

本示例演示如何使用 Java 开发一个 MCP (Model Context Protocol) 服务器,实现股票查询功能。该服务器可以获取指定股票代码的实时信息,包括当前价格、最高/最低价、开盘价、交易量和交易金额等。

1. 示例目标

我们将创建一个 MCP 服务器,实现以下功能:

  • 股票信息查询:通过股票代码查询实时股票信息,包括当前价格、最高/最低价、开盘价、交易量和交易金额等。
  • MCP 协议支持:实现 MCP 协议,使 AI 模型能够调用此服务器获取股票信息。
  • 2. 技术栈与核心依赖

    • Spring Boot 3.x
    • Spring AI MCP Server (用于实现 MCP 协议)
    • Spring Web (用于 HTTP 请求)
    • Maven (项目构建工具)

    在 pom.xml 中,你需要引入以下核心依赖:

    <dependencies>
    <!– Spring AI MCP Server 启动器 –>
    <dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server</artifactId>
    </dependency>

    <!– Spring Web 用于 HTTP 请求 –>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    </dependency>

    <!– Spring Boot 基础依赖 –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    </dependency>
    </dependencies>

    3. 项目配置

    在 src/main/resources/application.yml 文件中,配置 MCP 服务器信息。

    spring:
    main:
    web-application-type: none
    banner-mode: off
    ai:
    mcp:
    server:
    name: my-stock-server
    version: 0.0.1

    # NOTE: You must disable the banner and the console logging
    # to allow the STDIO transport to work !!!

    重要提示:MCP 服务器需要通过 STDIO 传输工作,因此必须禁用 Web 应用程序类型、横幅和控制台日志。

    4. 实现股票服务

    4.1 StockService.java

    实现股票信息查询的核心服务。

    package com.alibaba.cloud.ai.example.stock.service;

    import java.io.Serializable;
    import java.time.format.DateTimeFormatter;

    import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
    import com.fasterxml.jackson.annotation.JsonProperty;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.annotation.JsonSerialize;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    import org.springframework.ai.tool.annotation.Tool;
    import org.springframework.http.MediaType;
    import org.springframework.stereotype.Service;
    import org.springframework.web.client.RestClient;

    /**
    * Stock service for retrieving real-time stock information from Eastmoney API.
    * This service provides functionality to fetch stock data including current price,
    * high/low prices, opening price, trading volume, and amount.
    *
    */
    @Service
    public class StockService {
    private static final Logger logger = LoggerFactory.getLogger(StockService.class);
    private static final String BASE_URL = "https://push2.eastmoney.com/api/qt/stock/get";
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final ObjectMapper objectMapper = new ObjectMapper();

    private final RestClient restClient;

    public StockService() {
    this.restClient = RestClient.builder()
    .baseUrl(BASE_URL)
    .defaultHeader("Accept", MediaType.APPLICATION_JSON_VALUE)
    .build();
    }

    @JsonIgnoreProperties(ignoreUnknown = true)
    public record StockData(
    @JsonProperty("f43") Double currentPrice, // Latest price (in cents)
    @JsonProperty("f44") Double highPrice, // Highest price (in cents)
    @JsonProperty("f45") Double lowPrice, // Lowest price (in cents)
    @JsonProperty("f46") Double openPrice, // Opening price (in cents)
    @JsonProperty("f47") Double volume, // Trading volume (in lots)
    @JsonProperty("f48") Double amount, // Trading amount (in yuan)
    @JsonProperty("f57") String code, // Stock code
    @JsonProperty("f58") String name) { // Stock name
    }

    @JsonSerialize
    public record StockInfo(
    @JsonProperty("code") String code,
    @JsonProperty("name") String name,
    @JsonProperty("currentPrice") Double currentPrice,
    @JsonProperty("highPrice") Double highPrice,
    @JsonProperty("lowPrice") Double lowPrice,
    @JsonProperty("openPrice") Double openPrice,
    @JsonProperty("volume") Double volume,
    @JsonProperty("amount") Double amount
    ) implements Serializable {
    }

    @Tool(name = "getStockInfo", description = "Get real-time stock information for the specified stock code")
    public StockInfo getStockInfo(String stockCode) {
    try {
    // Validate stock code format
    if (!stockCode.matches("^[0-9]{6}$")) {
    throw new IllegalArgumentException("Stock code must be 6 digits");
    }

    logger.info("Fetching stock information for {}", stockCode);

    // Eastmoney API parameters
    String secid = stockCode.startsWith("6") ? "1." + stockCode : "0." + stockCode;

    String response = restClient.get()
    .uri(uriBuilder -> uriBuilder
    .queryParam("secid", secid)
    .queryParam("fields", "f43,f44,f45,f46,f47,f48,f57,f58")
    .build())
    .accept(MediaType.APPLICATION_JSON)
    .retrieve()
    .body(String.class);

    logger.info("Raw response: {}", response);

    JsonNode root = objectMapper.readTree(response);
    JsonNode data = root.path("data");

    if (data.isMissingNode()) {
    logger.warn("No stock data found");
    throw new IllegalArgumentException("No information found for stock code " + stockCode);
    }

    StockData stockData = objectMapper.treeToValue(data, StockData.class);
    logger.info("Parsed data: {}", stockData);

    if (stockData == null || stockData.name() == null) {
    throw new IllegalArgumentException("Invalid data format for stock code " + stockCode);
    }

    // Convert data format
    return new StockInfo(
    stockCode,
    stockData.name(),
    stockData.currentPrice() / 100.0, // Convert to yuan
    stockData.highPrice() / 100.0, // Convert to yuan
    stockData.lowPrice() / 100.0, // Convert to yuan
    stockData.openPrice() / 100.0, // Convert to yuan
    stockData.volume() / 10000.0, // Convert to 10,000 lots
    stockData.amount() / 100000000.0 // Convert to 100 million yuan
    );
    } catch (IllegalArgumentException e) {
    logger.error("Parameter error: {}", e.getMessage());
    throw e;
    } catch (Exception e) {
    logger.error("Failed to get stock {} information: {}", stockCode, e.getMessage(), e);
    throw new RuntimeException("Failed to get stock " + stockCode + " information: " + e.getMessage());
    }
    }
    }

    4.2 StockServerApplication.java

    实现主应用程序类,配置 MCP 服务器。

    package com.alibaba.cloud.ai.example.stock;

    import com.alibaba.cloud.ai.example.stock.service.StockService;

    import org.springframework.ai.tool.ToolCallbackProvider;
    import org.springframework.ai.tool.method.MethodToolCallbackProvider;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;

    @SpringBootApplication
    public class StockServerApplication {

    public static void main(String[] args) {
    SpringApplication.run(StockServerApplication.class, args);
    }

    @Bean
    public ToolCallbackProvider stockTools(StockService stockService) {
    return MethodToolCallbackProvider.builder().toolObjects(stockService).build();
    }
    }

    5. 实现原理与关键点

    5.1 MCP 服务器实现

    本示例的核心是使用 Spring AI MCP Server 框架实现一个 MCP 服务器。关键点包括:

    • 配置 MCP 服务器:在 application.yml 中配置服务器名称和版本。
    • 禁用 Web 应用程序:设置 web-application-type: none,使应用程序不启动 Web 容器。
    • 禁用横幅和控制台日志:设置 banner-mode: off,避免干扰 STDIO 传输。
    • 注册工具回调:通过 ToolCallbackProvider 将股票服务注册为 MCP 工具。

    5.2 股票数据获取

    股票数据通过东方财富 API 获取,关键实现点包括:

    • API 请求:使用 RestClient 向东方财富 API 发送 HTTP GET 请求。
    • 参数构造:根据股票代码构造 API 参数,上海证券交易所股票(6开头)使用 "1." 前缀,深圳证券交易所股票使用 "0." 前缀。
    • 数据解析:使用 Jackson 解析 JSON 响应,提取股票信息。
    • 数据转换:将 API 返回的数据(如价格以分为单位)转换为更易读的格式(如价格以元为单位)。

    5.3 工具注解

    使用 @Tool 注解将股票查询方法标记为 MCP 工具:

    @Tool(name = "getStockInfo", description = "Get real-time stock information for the specified stock code")
    public StockInfo getStockInfo(String stockCode) {
    // 方法实现
    }

    这个注解使 AI 模型能够识别并调用此方法,获取股票信息。

    6. 运行与测试

    6.1 构建和运行

    使用 Maven 构建并运行应用程序:

    # 构建项目
    mvn clean package

    # 运行应用程序
    java -jar target/starter-stock-server-0.0.1-SNAPSHOT.jar

    6.2 测试股票查询

    您可以通过以下方式测试股票查询功能:

  • 将 MCP 服务器连接到支持 MCP 协议的 AI 客户端。
  • 在 AI 客户端中请求查询股票信息,例如:"查询股票代码 000001 的信息"。
  • AI 客户端将通过 MCP 协议调用 getStockInfo 工具,并传入股票代码 "000001"。
  • 服务器将返回股票信息,包括当前价格、最高/最低价、开盘价、交易量和交易金额等。
  • 6.3 预期输出示例

    查询股票代码 "000001"(平安银行)的预期输出:

    {
    "code": "000001",
    "name": "平安银行",
    "currentPrice": 10.50,
    "highPrice": 10.65,
    "lowPrice": 10.45,
    "openPrice": 10.60,
    "volume": 125000.0,
    "amount": 1312500000.0
    }

    7. 扩展建议

    7.1 添加更多股票相关工具

    可以扩展此服务器,添加更多股票相关功能,例如:

    • 股票历史数据查询:添加查询历史股票价格的功能。
    • 股票筛选:添加根据特定条件筛选股票的功能。
    • 股票分析:添加基本的技术分析功能,如移动平均线、相对强弱指数等。

    7.2 添加其他金融工具

    除了股票,还可以添加其他金融工具的查询功能:

    • 基金查询:添加查询基金信息的功能。
    • 债券查询:添加查询债券信息的功能。
    • 汇率查询:添加查询汇率信息的功能。

    7.3 集成更多数据源

    可以集成多个数据源,提供更全面的金融信息:

    • 多数据源集成:集成多个金融数据提供商,提供更可靠的数据。
    • 数据缓存:添加缓存层,减少 API 调用次数,提高响应速度。
    • 数据验证:添加数据验证机制,确保数据的准确性和一致性。

    7.4 安全性和性能优化

    • API 密钥管理:如果使用需要 API 密钥的数据源,添加安全的密钥管理机制。
    • 请求限流:添加请求限流机制,避免超过数据源的 API 调用限制。
    • 错误处理:增强错误处理机制,提供更友好的错误信息。
    赞(0)
    未经允许不得转载:171主机测评 » 206-Spring AI Alibaba MCP 股票查询服务器示例
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址