工具调用MCP_Server 开发梳理

一、MCP 简介

MCP(Model Context Protocol)是 Spring AI 提供的工具调用协议,允许 AI 模型通过标准化接口调用外部工具。本项目是一个基于 Spring Boot 的图片搜索 MCP 服务器实现。

三、开发步骤

步骤 1:创建 Maven 项目

pom.xml 中添加以下核心依赖:

xml 复制代码
<!-- Spring Boot Starter -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

<!-- Spring AI MCP Server WebMVC 依赖 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>

<!-- Hutool 工具库(可选,用于HTTP请求和JSON处理) -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.37</version>
</dependency>

步骤 2:创建工具类

创建一个 Spring Service 类,并使用 @Tool 注解标记工具方法:

java 复制代码
package com.imagesearchmcpserver.tools;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;

@Service
public class ImageSearchTool {

    @Tool(description = "search image from web")
    public String searchImage(@ToolParam(description = "Search query keyword") String query) {
        // 实现工具逻辑
        return searchMediumImages(query).toString();
    }
}

关键注解说明:

注解 用途
@Tool 标记该方法为 MCP 工具,description 用于描述工具功能
@ToolParam 标记方法参数,description 用于描述参数含义
@Service 注册为 Spring Bean

步骤 3:配置工具提供者

在启动类中配置 ToolCallbackProvider Bean:

java 复制代码
@SpringBootApplication
public class YuImageSearchMcpServerApplication {

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

    @Bean
    public ToolCallbackProvider imageSearchTools(ImageSearchTool imageSearchTool) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(imageSearchTool)
                .build();
    }
}

步骤 4:配置应用

创建三种配置文件:

4.1 application.yml(默认配置)
yaml 复制代码
spring:
  application:
    name: image-search-mcp-server
  profiles:
    active: sse  # 默认使用 SSE 模式
server:
  port: 8127
4.2 application-sse.yml(SSE 模式)
yaml 复制代码
spring:
  ai:
    mcp:
      server:
        name: image-search-mcp-server
        version: 0.0.1
        type: SYNC
        stdio: false  # 启用 HTTP/SSE 模式
4.3 application-stdio.yml(标准输入输出模式)
yaml 复制代码
spring:
  ai:
    mcp:
      server:
        name: image-search-mcp-server
        version: 0.0.1
        type: SYNC
        stdio: true  # 启用标准输入输出模式
  main:
    web-application-type: none  # 非 Web 应用
    banner-mode: off            # 关闭启动 Banner

步骤 5:实现业务逻辑

在工具类中实现具体的业务逻辑。本项目以 Pexels 图片搜索 API 为例:

java 复制代码
public List<String> searchMediumImages(String query) {
    // 1. 设置请求头(包含 API 密钥)
    Map<String, String> headers = new HashMap<>();
    headers.put("Authorization", API_KEY);

    // 2. 设置请求参数
    Map<String, Object> params = new HashMap<>();
    params.put("query", query);

    // 3. 发送 HTTP 请求
    String response = HttpUtil.createGet(API_URL)
            .addHeaders(headers)
            .form(params)
            .execute()
            .body();

    // 4. 解析响应并提取图片 URL
    return JSONUtil.parseObj(response)
            .getJSONArray("photos")
            .stream()
            .map(photoObj -> (JSONObject) photoObj)
            .map(photoObj -> photoObj.getJSONObject("src"))
            .map(photo -> photo.getStr("medium"))
            .filter(StrUtil::isNotBlank)
            .collect(Collectors.toList());
}

四、启动方式

方式 1:SSE 模式(默认)

bash 复制代码
# 开发环境运行
./mvnw spring-boot:run

# 或打包后运行
./mvnw clean package
java -jar target/image-search-mcp-server-0.0.1-SNAPSHOT.jar

方式 2:Stdio 模式

bash 复制代码
./mvnw spring-boot:run -Dspring-boot.run.profiles=stdio

# 或
java -jar target/image-search-mcp-server-0.0.1-SNAPSHOT.jar --spring.profiles.active=stdio

五、两种运行模式对比

特性 SSE 模式 Stdio 模式
通信方式 HTTP + Server-Sent Events 标准输入/输出流
适用场景 远程服务、多客户端连接 本地进程调用、安全沙箱环境
Web 服务
配置参数 stdio: false stdio: true

六、MCP 工具调用流程

复制代码
┌─────────────────┐     HTTP/SSE      ┌──────────────────────┐
│   AI 客户端      │ ────────────────► │   MCP Server         │
│   (如 LlamaIndex)│                  │   (Spring Boot)      │
└─────────────────┘                  └──────────┬───────────┘
                                                │
                                                ▼
                                      ┌──────────────────────┐
                                      │  ToolCallbackProvider│
                                      │  发现并注册工具方法    │
                                      └──────────┬───────────┘
                                                │
                                                ▼
                                      ┌──────────────────────┐
                                      │   ImageSearchTool    │
                                      │  执行具体业务逻辑    │
                                      └──────────────────────┘

七、扩展开发

添加新工具

只需创建新的 Service 类并添加 @Tool 注解:

java 复制代码
@Service
public class WeatherTool {

    @Tool(description = "Get current weather")
    public String getWeather(@ToolParam(description = "City name") String city) {
        // 实现天气查询逻辑
        return "Weather in " + city + ": sunny";
    }
}

更新工具提供者

在启动类中添加新工具:

java 复制代码
@Bean
public ToolCallbackProvider tools(ImageSearchTool imageSearchTool, WeatherTool weatherTool) {
    return MethodToolCallbackProvider.builder()
            .toolObjects(imageSearchTool, weatherTool)
            .build();
}

八、注意事项

  1. API Key 管理:敏感信息如 API Key 应通过环境变量或配置中心管理,避免硬编码
  2. 错误处理:工具方法应妥善处理异常,返回友好的错误信息
  3. 参数校验:对输入参数进行必要的校验
  4. 日志记录:建议添加适当的日志记录便于排查问题
  5. 依赖版本:确保 Spring AI BOM 版本与 MCP Server 依赖版本一致

九、前端调用方式

9.1 前端项目结构

复制代码
ai-agent-frontend/
├── src/
│   ├── api/
│   │   └── index.js          # API 封装与 SSE 连接
│   ├── components/
│   │   └── ChatRoom.vue      # 聊天组件
│   ├── views/
│   │   └── SuperAgent.vue    # AI 超级智能体页面
│   └── main.js               # 入口文件
├── package.json
└── vite.config.js

9.2 核心调用流程

前端通过 SSE (Server-Sent Events) 与后端建立实时通信,实现流式响应。

9.2.1 SSE 连接封装
javascript 复制代码
// src/api/index.js
import axios from 'axios'

const API_BASE_URL = process.env.NODE_ENV === 'production' 
  ? '/api' 
  : 'http://localhost:8123/api'

export const connectSSE = (url, params, onMessage, onError) => {
  const queryString = Object.keys(params)
    .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
    .join('&')
  
  const fullUrl = `${API_BASE_URL}${url}?${queryString}`
  
  const eventSource = new EventSource(fullUrl)
  
  eventSource.onmessage = event => {
    let data = event.data
    if (data === '[DONE]') {
      if (onMessage) onMessage('[DONE]')
    } else {
      if (onMessage) onMessage(data)
    }
  }
  
  eventSource.onerror = error => {
    if (onError) onError(error)
    eventSource.close()
  }
  
  return eventSource
}
9.2.2 调用 AI 聊天接口
javascript 复制代码
// AI超级智能体聊天
export const chatWithManus = (message) => {
  return connectSSE('/ai/manus/chat', { message })
}

9.3 组件层面调用

在 Vue 组件中使用 SSE 连接:

javascript 复制代码
// src/views/SuperAgent.vue
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { chatWithManus } from '../api'

const messages = ref([])
const connectionStatus = ref('disconnected')
let eventSource = null

const sendMessage = (message) => {
  // 添加用户消息
  addMessage(message, true, 'user-question')
  
  // 关闭之前的连接
  if (eventSource) {
    eventSource.close()
  }
  
  connectionStatus.value = 'connecting'
  
  let messageBuffer = []
  
  eventSource = chatWithManus(message)
  
  eventSource.onmessage = (event) => {
    const data = event.data
    
    if (data && data !== '[DONE]') {
      messageBuffer.push(data)
      
      // 根据标点或长度判断是否创建新消息气泡
      const lastChar = data.charAt(data.length - 1)
      const hasCompleteSentence = ['。', '!', '?', '...'].includes(lastChar)
      const isLongEnough = messageBuffer.join('').length > 40
      
      if (hasCompleteSentence || isLongEnough) {
        addMessage(messageBuffer.join(''), false, 'ai-answer')
        messageBuffer = []
      }
    }
    
    if (data === '[DONE]') {
      if (messageBuffer.length > 0) {
        addMessage(messageBuffer.join(''), false, 'ai-final')
      }
      connectionStatus.value = 'disconnected'
      eventSource.close()
    }
  }
  
  eventSource.onerror = (error) => {
    connectionStatus.value = 'error'
    eventSource.close()
  }
}

9.4 完整调用链路

复制代码
┌─────────────────┐     HTTP/SSE      ┌──────────────────────┐     调用工具     ┌──────────────────────┐
│   Vue 前端      │ ────────────────► │   后端 API Gateway   │ ─────────────► │   MCP Server         │
│  SuperAgent.vue │   /ai/manus/chat  │   (端口 8123)        │   /mcp/tools    │   (端口 8127)        │
└─────────────────┘                  └──────────────────────┘                  └──────────────────────┘
       ▲                                      │                                      │
       │                                      │                                      │
       └───────────  SSE 流式响应  ◄───────────┴───────────  工具执行结果  ◄───────────┘

9.5 前端配置说明

配置项 开发环境 生产环境
API 地址 http://localhost:8123/api /api
通信方式 SSE (EventSource) SSE (EventSource)
超时时间 60秒 60秒

9.6 注意事项

  1. 跨域处理:后端需配置 CORS 允许前端域名访问
  2. 连接管理:发送新消息前需关闭旧连接,避免重复响应
  3. 消息缓冲:SSE 可能分段传输,需缓冲后按语义合并
  4. 异常处理 :监听 onerror 事件,优雅处理连接断开
  5. 资源清理 :组件销毁前调用 eventSource.close() 释放资源

相关推荐
lili00121 小时前
2026 企业 AI 选型新范式:OpenRouter Fusion 证明多模型融合性价比远超单模型,企业该如何重构技术栈? - 微元算力(weytoken)
java·人工智能·python·重构·ai编程
shushangyun_1 小时前
汽车服务行业B2B平台+AI解决方案哪家专业:2026年最新测评
java·运维·网络·数据库·人工智能·汽车
A.说学逗唱的Coke1 小时前
【大模型专题】Spring AI Alibaba × Skill 整合实战:让 AI 真正“会干活
java·人工智能·spring
米小虾1 小时前
AI Agent 记忆系统:从对话记录到认知架构
人工智能·agent
-山中问答-1 小时前
【智能体工具使用实战08】实战项目:代码仓库健康度分析Agent
人工智能·智能体·工具调用·工程实战
林间码客1 小时前
05 逻辑斯蒂回归(Logistic Regression)
人工智能·数据挖掘·回归
米小虾2 小时前
AI Agent 上下文管理:从窗口到世界的桥梁
人工智能·agent
Gavynlee2 小时前
ubuntu22.04配置hermes(API以硅基流动为例)
人工智能
渡众机器人2 小时前
第八届全球校园人工智能算法精英大赛-算法应用赛-渡众机器人智能体对抗挑战赛规则
人工智能·算法·机器人·自动驾驶·自主导航·对抗赛