什么是模型上下文协议(MCP)?

MCP(模型上下文协议)是一个开源标准,用于将人工智能应用与外部系统连接起来。通过MCP,像Claude或ChatGPT这样的AI应用可以连接数据源(如本地文件、数据库)、工具(如搜索引擎、计算器)和工作流程(如专门提示)——使它们能够访问关键信息并执行任务。可以把MCP想象成AI应用的USB-C接口。正如USB-C提供了连接电子设备的标准化方式,MCP也提供了将AI应用与外部系统连接的标准化方式。

MCP的文档:What is the Model Context Protocol (MCP)? - Model Context Protocol

开始构建自己的MCP

选择SDK

MCP提供了多种编程语言Q的支持,如Python,Java,Kotin,TypeScrip等。这里以Java SDK为例

SDK主要关注

  • Stdio(标准输入输出)

    • 主要是用于本地部署,不需要联网,大模型也在本地直接访问

  • WebMVC

    • 基于 Spring MVC

    • 同步阻塞

    • 一个连接占一个线程,主要用于小规模,自用,默认使用Tomcat连接,默认的连接池数量足够使用

  • WebFlux

    • 异步非阻塞模型(Spring WebFlux,基于 Netty)

    • 少量线程处理大量请求

    • 主要用于高并发,对外使用

其他语言的SDK:SDKs - Model Context Protocol

多种传输方式

MCP(Model Control Protocol)提供的4 种传输方式,本质只有 2 大类

1. Stdio(标准输入输出)

= 本地进程内通信

= 像命令行一样,程序之间直接传数据

= 不走网络

= 最快、最简单、最安全

2. SSE(Server-Sent Events)

= 网络通信

= 服务器 → 客户端单向实时推消息

= 走 HTTP 长连接

= 分 3 种实现:HTTP、WebMVC、WebFlux


4 种传输方式到底是什么?(超级大白话)
1)Stdio(默认)
  • 不联网

  • 两个程序在同一台机器上,直接互相读写数据

  • 像你在终端输入命令,程序直接输出结果

  • 最快、最轻量

  • 适合:本地工具、本地插件

2)HTTP SSE(基础版)
  • 最原始的 SSE

  • 简单 HTTP 长连接

  • 不依赖任何框架

  • 适合:简单场景、轻量服务

3)WebMVC SSE(传统 Spring)
  • 基于 Spring MVC

  • 同步阻塞

  • 一个连接占一个线程

  • 适合:传统企业项目、并发不高的场景

4)WebFlux SSE(响应式 Spring)
  • 基于 Spring WebFlux

  • 异步非阻塞

  • 少量线程可以支持海量连接

  • 适合:高并发、实时推送、微服务

最核心区别
传输 类型 线程模型 依赖 适合场景
Stdio 本地进程通信 本地、最快
HTTP SSE 网络长连接 简单 无框架 简单服务
WebMVC SSE 网络长连接 同步阻塞 Spring MVC 传统项目
WebFlux SSE 网络长连接 异步非阻塞 Spring WebFlux 高并发

其他语言(比如 Python)也是这样区分吗?
答案:思想完全一样!只是名字不同!

不管是 Java、Python、Go、JS,MCP 传输永远分两大类:

1)本地通信:Stdio(所有语言都支持)
  • Python:subprocess + stdin/stdout

  • Go:os.Stdin, os.Stdout

  • JS:process.stdin / process.stdout

2)网络通信:SSE(所有语言都能实现)

Python 里对应的是:

  • Flask SSE

  • Django SSE

  • FastAPI SSE(异步,对应 WebFlux)

  • 普通 HTTP SSE(对应 WebMVC)

案例

MCP服务器
依赖
基本环境

jdk17

maven3.9.4

springboot3.3.0
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

<dependency>
     <groupId>org.springframework.ai</groupId>
     <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
     <version>1.1.0-M1-PLATFORM-2</version>
</dependency>

注意:如果使用其他的版本例如1.1.0.RC1等,依赖自己会自动扫描注入工具,我们手写的Bean也会注入,会导致有一份注入失败,但是不影响,只是在日志会有告警

org.springaicommunity.mcp...AsyncMcpToolProvider → No tool methods found: []  ← 社区版(空的)
o.springframework.ai.mcp...McpServerAutoConfiguration → Registered tools: 2   ← 官方版(有工具)
application.yml配置文件:
server:
  port: 8888

spring:
  application:
    name: foss-mcp-server

  ai:
    mcp:
      server:
        name: foss-mcp-server
        version: 1.0.0
        type: ASYNC
        sse-message-endpoint: /message
        sse-endpoint: /sse

logging:
  level:
    root: INFO
    org.springframework.ai.mcp: DEBUG
    org.springframework.web.servlet: DEBUG
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: DEBUG
    org.springframework.web.servlet.mvc.method.annotation.SseEmitter: TRACE
    com.cctv.mcp: DEBUG

config配置文件:作用就是将工具注入

import com.cctv.mcp.service.impl.FossBucketService;
import com.cctv.mcp.service.impl.FossMonitoringStorage;
import com.cctv.mcp.service.impl.FossObjectService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ToolConfig {

    @Bean
    public ToolCallbackProvider toolCallbackProvider(
            FossObjectService fossObjectService) {
        System.out.println(">>> [CONFIG] ToolCallbackProvider 正在初始化...");
        System.out.println(">>> [CONFIG] FossService 实例:" + fossObjectService);
        // 👇 添加这行日志,确认服务启动时拿到了对象
        System.out.println(">>> [DEBUG] FossService injected: " + fossObjectService.getClass().getName());

        // 👇 手动检查一下类里有没有 @Tool 注解的方法 (可选高级调试)
        java.lang.reflect.Method[] methods = fossObjectService.getClass().getDeclaredMethods();
        for (java.lang.reflect.Method m : methods) {
            if (m.isAnnotationPresent(org.springframework.ai.tool.annotation.Tool.class)) {
                System.out.println(">>> [DEBUG] Found @Tool method: " + m.getName());
            } else {
                // 看看是不是因为导包错导致注解不匹配
                for (var ann : m.getAnnotations()) {
                    if (ann.annotationType().getSimpleName().equals("Tool")) {
                        System.out.println(">>> [DEBUG] Found a 'Tool' annotation but class mismatch: " + ann.annotationType().getName());
                    }
                }
            }
        }
        return MethodToolCallbackProvider.builder()
                .toolObjects(
                        fossObjectService) // 注入你的工具类
                .build();
    }
}
具体的工具类:
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Component;

@Component
public class FossObjectService {

    @Tool(name = "hello", description = "搜索开源软件信息") // 显式指定 name
    public String hello(String name) {
        System.out.println(">>> [TOOL EXEC] 收到工具调用!参数 name=" + name);
        try {
            Thread.sleep(100); // 模拟一点点耗时
        } catch (InterruptedException e) {}
        String result = "Hello, " + name + "! 调用成功啦!";
        System.out.println(">>> [TOOL EXEC] 执行完毕,返回:" + result);
        return result;
    }
}

方法的返回类型:推荐是string字符串

mcp支持
① Text(文本)     — 最常用
② Image(图片)    — 返回图片数据
③ Resource(资源) — 返回文件/资源引用

启动类:springboot的启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
@ServletComponentScan
public class McpServerSpringAiApplication {
    public static void main(String[] args) {
        SpringApplication.run(McpServerSpringAiApplication.class, args);
    }

}

到此为止就可以开启服务测试工具了,测试有多种方法:使用postman直接测试接口,使用mcp客户端测试,使用现成的桌面工具如Claude Desktop、Cursor Q等来引入我们的MCP Server。我自己试了下Claude,但是Claude需要国外的手机号注册。

所以本次使用手动编写MCP客户端代码测试
依赖
<dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-mcp-client</artifactId>
      <version>1.1.2</version>
</dependency>
测试类:

import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
import io.modelcontextprotocol.spec.McpClientTransport;
import io.modelcontextprotocol.spec.McpSchema;

import java.time.Duration;
import java.util.List;
import java.util.Map;

public class McpTestClient {

    public static void main(String[] args) throws Exception {

        // ✅ 用 builder 创建
        HttpClientSseClientTransport transport = HttpClientSseClientTransport
                .builder("http://localhost:8888")
                .sseEndpoint("/sse")
                .build();

        // 2. 创建客户端(自动完成 initialize 握手)
        var client = McpClient.sync(transport)
                .requestTimeout(Duration.ofSeconds(30))
                .build();

        client.initialize();

        System.out.println("=== 连接成功 ===\n");

        // 3. 查看所有工具
        List<McpSchema.Tool> tools = client.listTools().tools();
        System.out.println("=== 可用工具 ===");
        for (McpSchema.Tool tool : tools) {
            System.out.println("工具名: " + tool.name());
            System.out.println("描述: " + tool.description());
            System.out.println("参数: " + tool.inputSchema());
            System.out.println("---");
        }

        // 4. 调用工具(不需要大模型,直接传参)
        System.out.println("\n=== 调用 hello 工具 ===");
        McpSchema.CallToolResult result = client.callTool(
                new McpSchema.CallToolRequest("hello",
                        Map.of("name", "张三"))
        );

        // 5. 打印返回结果
        for (McpSchema.Content content : result.content()) {
            System.out.println("返回内容: " + content);
        }

        // 7. 关闭连接
        client.close();
    }
}

然后直接启动服务,运行MCP客户端的main方法即可!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐