什么是模型上下文协议(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社区

更多推荐