我们上一节知道了怎么创建和定义可调用的工具,但是你会发现这些工具的使用都是在我们的AI应用内部的,我们对工具的改动都相当于是改了我们的应用,工具的调用怎么看都像是一种扩展功能,一种可随时插拔的插件式的东西
所以就有了 MCP

MCP 的定义

MCP 翻译过来就是模型上下文协议(Model Co‏‏‏‏ntext Protocol),就是一种标准,造一个能被AI调用的工具不是什么新鲜事,但要互通要有标准啊,像是现实中的造车、食品等方面都有国家标准,所以有了 MCP 就相当于给可插拔的工具调用说明了是使用的 USB 还是 type-c,标准可以造就生态

了解 MCP 的架构

MCP 的核心是 “‏‏客户‏‏端 - 服务器” 架构,其中 MCP‍‍ 客户端主机可以‍‍连接到多个服务器,服务端也可以为多个客户端提供服务,客户端‎‎主机是指希望访问 MCP 服‎‎务的程序,像是Claude code、trae等

我们先说MCP 客户端

MCP Client 主要负责和 MCP‍‍‍‍ 服务器建立连接并进行通信,为了适应不同场景,它提供⁡⁡⁡⁡了多种数据传输方式,包括:

  • Stdio 标准输入 / 输出:适用于本地调用
  • 基于 Java HttpClient 和 WebFlux 的 SSE 传输:适用于远程调用

(这里简单补充一下stdio 模式和SSE 模式
stdio 就是 标准输入 / 标准输出
意思是客户端直接启动一个本地程序,然后通过这个程序的命令行输入输出跟它通信
SSE 是 Server-Sent Events
意思是 MCP 服务端自己作为一个 Web 服务跑起来,客户端通过 URL 连过去通信)

再说说MCP 服务端

MCP S‏‏‏‏erver 主要用来‏‏‏‏为客户端提供各种工⁡⁡⁡⁡具、资源和功能支持的,和客户端一样,它也‏‏‏‏可以通过多种方式进行数据传输,比如‍‍‍‍ Stdio 标准输入 / 输出、‎‎‎‎基于 Servlet / WebF‏‏‏‏lux / WebMVC 的 SS⁡⁡⁡⁡E 传输,满足不同应用场景

最简单的使用

一些AI工具(像是trae)都提供了自定义添加MCP服务的功能,大多数 MCP 服务都支持基于 NPX 工具运行,添加几行配置就行

(这里补充一下 Node.js 中 npx 工具的使用
npx 是 Node.js 生态里“临时运行一个包里的命令”的工具
先通过 npx 找这个 npm 包
本地没装时,会先下载到缓存,而不是使用的项目里
然后直接把这个包里的可执行命令跑起来)

若要是想在自己的项目中使用 MCP
(也可以说是自己项目添加一个MCP客户端了)

1.首先引入依赖

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
    <version>1.0.0-M6</version>
</dependency>


2.在 resources 目录下新建 mcp-servers.json 配置

{
  "mcpServers": {
    "amap-maps": {
      "command": "npx",
      "args": [
        "-y",
        "@amap/amap-maps-mcp-server"
      ],
      "env": {
        "AMAP_MAPS_API_KEY": "改成你的 API Key"
      }
    }
  }
}

3.修改 Spr‏‏‏‏ing 配置文件

spring:
    ai:
      mcp:
        client:
          stdio: // 本地运行使用stdio模式,且要指定配置文件位置
            servers-configuration: classpath:mcp-servers.json

4.添加代码,新增一个利用 MCP 完成对话的方法

@Resource
private ToolCallbackProvider toolCallbackProvider;
// 通过自动注入的 ToolCallbackProvider 获取到配置中定义的 MCP 服务提供的 所有工具
public String doChatWithMcp(String message, String chatId) {
ChatResponse response = chatClient
            .prompt()
            .user(message)
            .tools(toolCallbackProvider) // 像工具调用一样使用
            .call()
            .chatResponse();
    String content = response.getResult().getOutput().getText();
return content;
}

从这段代码我们能够看出,MCP 调用的本质就是类似工具调用( .tools(toolCallbackProvider) )
那接下来我们就进入

自己开发一个MCP服务

首先是 MCP 客户端开发

(其实与上面说的自己项目中使用MCP一样,不过这里再说的详细一点吧)

1.引入依赖(spring-ai-starter-mcp-client:核心启动器,提供 STDIO 和基于 HTTP 的 SSE 支持)
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
</dependency>

2.配置连接

用spring配置+json文件(目前仅支持 stdio 连接)

spring:
  ai:
    mcp:
      client:
        stdio:
          servers-configuration: classpath:mcp-servers.json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Users/username/Desktop",
        "/Users/username/Downloads"
      ]
    }
  }
}

直接写‏‏‏‏入配置文件,‍这‍种‍方‍式同时‎支持‎ s‎td‎‏io ‏和 S‏S⁡E 连⁡‏接方式

spring:
  ai:
    mcp:
      client:
        enabled: true
        name: my-mcp-client
        version: 1.0.0
        request-timeout: 30s
        type: SYNC
        sse:
          connections:
            server1:
              url: http://localhost:8080
        stdio:
          connections:
            server1:
              command: /path/to/server
              args:
                - --port=8080
              env:
                API_KEY: your-api-key
3.使用服务
// 和 Spring AI 的工具进行整合
@Autowired
private SyncMcpToolCallbackProvider toolCallbackProvider;
// 这里可以真的把MCP当做工具注册的
ToolCallback[] toolCallbacks = toolCallbackProvider.getToolCallbacks();
ChatResponse response = chatClient
        .prompt()
        .user(message)
        .tools(toolCallbackProvider)
        .call()
        .chatResponse();
4.补充:同步和异步客户端

Spring AI 同时支持 同步和异步客户端类型,可根据应用需求选择合适的模式,只需要更改配置即可:spring.ai.mcp.client.type=ASYNC

(type=ASYNC 说的是 底层客户端是异步实现;
但你自己的代码如果还是“发出请求后立刻等结果”,那 整体业务看起来仍然是同步的;
就相当于是两个关口,业务代码到底层客户端可以用代码控制是用排队还是等着,底层客户端到MCP服务用配置可以控制排队和等着)

好,接下来才是重点

MCP 服务端开发(图片搜索服务示例,用Pexels网站常规搜索接口)

(当然你要先去pexels网站注册并获取自己的API密钥才可以免费调用)

1.引入依赖(spring-ai-starter-mcp-server-webmvc:提供基于 Spring MVC 的 SSE 传输和可选的 stdio 传输,一般建议引入这个)
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
</dependency>

2.配置文件

编写主配置文件 application.yml,可以灵活指定激活哪套配置

spring:
  application:
    name: image-search-mcp-server
  profiles:
    active: stdio // 或是sse
server:
  port: 8127
stdio 配置文件 application-stdio.yml(需关闭 web 支持)
spring:
  ai:
    mcp:
      server:
        name: yu-image-search-mcp-server
        version: 0.0.1
        type: SYNC
        #stdio
        stdio: true
  #stdio
  main:
    web-application-type: none
    banner-mode: off
SSE 配置文件 application-sse.yml(需关闭 stdio 模式)
spring:
  ai:
    mcp:
      server:
        name: yu-image-search-mcp-server
        version: 0.0.1
        type: SYNC
        #sse
        stdio: false
3.编写服务类(其实和之前的@Tool定义工具调用一样的)
@Service
public class ImageSearchTool {
// 替换为你的 Pexels API 密钥(需从官网申请)
private static final String API_KEY = "你的 API Key";

// Pexels 常规搜索接口(请以文档为准)
private static final String API_URL = "https://api.pexels.com/v1/search";

@Tool(description = "search image from web")
public String searchImage(@ToolParam(description = "Search query keyword") String query) {
    try {
        return String.join(",", searchMediumImages(query));
    } catch (Exception e) {
        return "Error search image: " + e.getMessage();
    }
}

/**
 * 搜索中等尺寸的图片列表
 *
 * @param query
 * @return
 */
public List<String> searchMediumImages(String query) {
    // 设置请求头(包含API密钥)
    Map<String, String> headers = new HashMap<>();
    headers.put("Authorization", API_KEY);

    // 设置请求参数(仅包含query,可根据文档补充page、per_page等参数)
    Map<String, Object> params = new HashMap<>();
    params.put("query", query);

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

    // 解析响应JSON(假设响应结构包含"photos"数组,每个元素包含"medium"字段)
    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());
    }
}
4.在主类中通过定义 ToolCallbackProvider Bean 来注册工具(放配置类也行)
@Bean
public ToolCallbackProvider imageSearchTools(ImageSearchTool imageSearchTool) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(imageSearchTool)
                .build();
}

然后就可以用SSE模式:先启动这个MCP服务端,然后客户端用url的方式访问;
或是stdio模式:打包成jar包,然后客户端用stdio用配置文件中的命令启动这个jar包,再访问
客户端stdio方式中

stdio模式中mcp-servers.json 配置文件及相关命令
{
  "mcpServers": {
    "yu-image-search-mcp-server": {
      "command": "java",
      "args": [
        "-Dspring.ai.mcp.server.stdio=true",
        "-Dspring.main.web-application-type=none",
        "-Dlogging.pattern.console=",
        "-jar",
        "服务端jar包的位置image-search-mcp-server-0.0.1-SNAPSHOT.jar"
      ],
      "env": {}
    }
  }
}

最后说说题外话:同样是可以实现工具的调用,skills出来后MCP好像都听不见声了

事实就是这样,实现方式上skills只是一个文件夹,有时候里面还只有提示词连程序脚本都没有,可使用起来人们就是偏向skills,要我说skills相比MCP有一个巨大的优点:就是简单,没错,就是一些没有技术背景的人都完全可以开源一个自己的skills,这就是一个巨大的生态啊

当然关于skills与MCP的区别以及是否真的会取代的问题,不会那么简单,也许我可以专门出一个文章好好聊聊

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐