图解源码:万字解析 Spring AI 到底是怎么实现 MCP 的(源码部分)

由于篇幅问题,源码部分在这里新建了一篇文章,完整的文章在这里:juejin.cn/spost/75709...

3.1 客户端

3.1.1 入口层

MCP 客户端入口层涉及到的类包括 McpClientMcpClient.AsyncSpc (内部类)、McpClient.SyncSpec(内部类),如下图所示:

在入口层中,McpClient 是创建 MCP 客户端的统一入口,通过静态方法 sync()async() 提供了同步和异步两种模式的构建方式,分别返回 SyncSpecAsyncSpec 这两个内部构建器类,用于配置客户端实例的各项参数并最终生成对应的 McpSyncClientMcpAsyncClient

其中,AsyncSpecSyncSpec 的设计非常相似,都封装了配置信息,且它们内部的方法(如 requestTimeout()initializationTimeout()capabilities())都只是对构建器内成员变量的简单赋值。而不同的是,异步模式在处理服务端的变更通知时,是基于 Reactor 的 Mono 进行响应式调用的,而同步模式则使用传统的 Consumer 回调方式。

核心的参数描述如下:

  • transport:传输层实现对象。
  • requestTimeout:请求超时时间,默认 20s。
  • initializationTimeout:初始化超时时间,默认 20s。
  • capabilities:客户端能力配置,包含实验性功能、根功能、采样能力等配置。
  • clientInfo:客户端基本信息,包含客户端名字和版本。
  • toolsChangeConsumers:工具变更通知的消费者列表,用于保存当服务端工具列表变化时要异步执行的回调函数列表。

核心的源码及注释如下所示:

Java 复制代码
/**
 * 创建 MCP 客户端的工厂类
 */
public interface McpClient {

    /**
     * 创建 MCP 同步客户端
     *
     * @param transport 传输层对象
     * @return 同步客户端构建器
     */
    static SyncSpec sync(McpClientTransport transport) {
        return new SyncSpec(transport);
    }

    /**
     * 创建 MCP 异步客户端
     *
     * @param transport 传输层对象
     * @return 异步客户端构建器
     */
    static AsyncSpec async(McpClientTransport transport) {
        return new AsyncSpec(transport);
    }

    /**
     * 同步客户端构建器
     */
    class SyncSpec {

        // 客户端传输层对象
        private final McpClientTransport transport;

        // 请求超时时间
        private Duration requestTimeout = Duration.ofSeconds(20);

        // 初始化超时时间
        private Duration initializationTimeout = Duration.ofSeconds(20);

        // 客户端能力配置
        private ClientCapabilities capabilities;

        // 客户端实现信息
        private Implementation clientInfo = new Implementation("Java SDK MCP Client", "1.0.0");

        // 工具变更通知的消费者列表
        private final List<Consumer<List<McpSchema.Tool>>> toolsChangeConsumers = new ArrayList<>();

        // 其他属性省略...

        private SyncSpec(McpClientTransport transport) {
            Assert.notNull(transport, "Transport must not be null");
            this.transport = transport;
        }

        /**
         * 设置请求超时时间
         */
        public SyncSpec requestTimeout(Duration requestTimeout) {
            Assert.notNull(requestTimeout, "Request timeout must not be null");
            this.requestTimeout = requestTimeout;
            return this;
        }

        /**
         * 设置初始化超时时间
         */
        public SyncSpec initializationTimeout(Duration initializationTimeout) {
            Assert.notNull(initializationTimeout, "Initialization timeout must not be null");
            this.initializationTimeout = initializationTimeout;
            return this;
        }

        /**
         * 设置客户端能力配置
         */
        public SyncSpec capabilities(ClientCapabilities capabilities) {
            Assert.notNull(capabilities, "Capabilities must not be null");
            this.capabilities = capabilities;
            return this;
        }

        /**
         * 设置客户端实现信息
         */
        public SyncSpec clientInfo(Implementation clientInfo) {
            Assert.notNull(clientInfo, "Client info must not be null");
            this.clientInfo = clientInfo;
            return this;
        }

        /**
         * 设置客户端工具变更通知的消费者列表
         */
        public SyncSpec toolsChangeConsumer(Consumer<List<McpSchema.Tool>> toolsChangeConsumer) {
            Assert.notNull(toolsChangeConsumer, "Tools change consumer must not be null");
            this.toolsChangeConsumers.add(toolsChangeConsumer);
            return this;
        }

        /**
         * 构建同步客户端实例
         */
        public McpSyncClient build() {
            // 由于「同步」客户端依赖于「异步」客户端,因此这里会先创建「异步」客户端,再将其作为「同步」客户端的属性
            McpClientFeatures.Sync syncFeatures = new McpClientFeatures.Sync(this.clientInfo, this.capabilities,
                    this.roots, this.toolsChangeConsumers, this.resourcesChangeConsumers, this.promptsChangeConsumers,
                    this.loggingConsumers, this.samplingHandler);
            McpClientFeatures.Async asyncFeatures = McpClientFeatures.Async.fromSync(syncFeatures);

            return new McpSyncClient(
                    new McpAsyncClient(transport, this.requestTimeout, this.initializationTimeout, asyncFeatures));
        }

        // 其他方法省略...
    }

    /**
     * 异步客户端构建器
     */
    class AsyncSpec {
        // 异步构建器的属性和方法也是类似的

        private final McpClientTransport transport;

        private Duration requestTimeout = Duration.ofSeconds(20);

        private Duration initializationTimeout = Duration.ofSeconds(20);

        private ClientCapabilities capabilities;

        private Implementation clientInfo = new Implementation("Spring AI MCP Client", "0.3.1");


        private final List<Function<List<McpSchema.Tool>, Mono<Void>>> toolsChangeConsumers = new ArrayList<>();

        // 其他属性省略...

        private AsyncSpec(McpClientTransport transport) {
            Assert.notNull(transport, "Transport must not be null");
            this.transport = transport;
        }

        public AsyncSpec requestTimeout(Duration requestTimeout) {
            Assert.notNull(requestTimeout, "Request timeout must not be null");
            this.requestTimeout = requestTimeout;
            return this;
        }

        public AsyncSpec initializationTimeout(Duration initializationTimeout) {
            Assert.notNull(initializationTimeout, "Initialization timeout must not be null");
            this.initializationTimeout = initializationTimeout;
            return this;
        }

        public AsyncSpec capabilities(ClientCapabilities capabilities) {
            Assert.notNull(capabilities, "Capabilities must not be null");
            this.capabilities = capabilities;
            return this;
        }

        public AsyncSpec clientInfo(Implementation clientInfo) {
            Assert.notNull(clientInfo, "Client info must not be null");
            this.clientInfo = clientInfo;
            return this;
        }

        public AsyncSpec toolsChangeConsumer(Function<List<McpSchema.Tool>, Mono<Void>> toolsChangeConsumer) {
            Assert.notNull(toolsChangeConsumer, "Tools change consumer must not be null");
            this.toolsChangeConsumers.add(toolsChangeConsumer);
            return this;
        }

        public McpAsyncClient build() {
            return new McpAsyncClient(this.transport, this.requestTimeout, this.initializationTimeout,
                    new McpClientFeatures.Async(this.clientInfo, this.capabilities, this.roots,
                            this.toolsChangeConsumers, this.resourcesChangeConsumers, this.promptsChangeConsumers,
                            this.loggingConsumers, this.samplingHandler));
        }

        // 其他方法省略...
    }
}

3.1.2 运行层

MCP 客户端运行层涉及到的类包括 McpSyncClientMcpAsyncClient,如下图所示:

在运行层中,McpSyncClientMcpAsyncClient 分别是 MCP 同步客户端和异步客户端的执行核心,它们共同承担了与服务端交互的实际逻辑。从类图中可以看出,同步客户端并没有独立实现实际的业务逻辑,而是通过组合的方式持有一个异步客户端实例,并将所有核心方法调用委托给它执行 。在其源码中,initialize()ping()callTool() 等方法都是通过调用异步客户端对应的方法,再通过阻塞的方式获取结果的。也就是说,同步客户端只是异步客户端的一层包装

异步客户端是整个运行层的逻辑核心,内部封装了 MCP 会话、能力配置、传输层等多个核心对象:

  • initialized:表示是否初始化完成。
  • mcpSession:客户端与服务端通信的上下文对象,封装了消息发送、传输通道关闭等底层逻辑。
  • clientCapabilities/serverCapabilities:分别表示客户端与服务端的功能能力,如客户端是否支持采样,服务端是否支持工具调用、Prompt 管理、资源同步等。
  • clientInfo/serverInfo:分别表示客户端与服务端的基本信息,如名称和版本。
  • transport:底层传输通道(如 Stdio、SSE 等),屏蔽了具体的协议细节。

主要方法包括:

  • initialize():负责完成 MCP 客户端的初始化过程,建议通信会话、同步能力信息。
  • ping():发送一个心跳请求以验证连接状态,用于检测服务端是否仍处于可用状态。
  • callTool():调用服务端注册的某个工具。
  • listTools():向服务端请求来获取可用的工具列表。
  • close():立即关闭客户端连接和会话资源。
  • closeGracefully():执行优雅关闭操作,确保未完成的请求正常结束。

核心的源码及注释如下所示:

Java 复制代码
/**
 * MCP 异步客户端
 */
public class McpAsyncClient {

    // 是否初始化完成
    private AtomicBoolean initialized = new AtomicBoolean(false);

    // 会话层对象
    private final McpClientSession mcpSession;

    // 客户端能力配置
    private final McpSchema.ClientCapabilities clientCapabilities;

    // 服务端能力配置
    private McpSchema.ServerCapabilities serverCapabilities;

    // 传输层对象
    private final McpTransport transport;

    // 其他属性省略...

    /**
     * MCP 异步客户端构造方法
     *
     * @param transport 传输层对象
     * @param requestTimeout 会话中请求-响应的超时时间
     * @param initializationTimeout 客户端-服务器等待的最大超时时间
     * @param features MCP 客户端支持的特性
     */
    McpAsyncClient(McpClientTransport transport, Duration requestTimeout, Duration initializationTimeout,
                   McpClientFeatures.Async features) {

        Assert.notNull(transport, "Transport must not be null");
        Assert.notNull(requestTimeout, "Request timeout must not be null");
        Assert.notNull(initializationTimeout, "Initialization timeout must not be null");

        this.clientInfo = features.clientInfo();
        this.clientCapabilities = features.clientCapabilities();
        this.transport = transport;
        this.roots = new ConcurrentHashMap<>(features.roots());
        this.initializationTimeout = initializationTimeout;

        // 请求处理器 key: 方法名,value: 处理器
        Map<String, RequestHandler<?>> requestHandlers = new HashMap<>();

        if (this.clientCapabilities.roots() != null) {
            requestHandlers.put(McpSchema.METHOD_ROOTS_LIST, rootsListRequestHandler());
        }

        if (this.clientCapabilities.sampling() != null) {
            if (features.samplingHandler() == null) {
                throw new McpError("Sampling handler must not be null when client capabilities include sampling");
            }
            this.samplingHandler = features.samplingHandler();
            requestHandlers.put(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE, samplingCreateMessageHandler());
        }

        // 通知处理器,key: 方法名,value: 处理器
        Map<String, NotificationHandler> notificationHandlers = new HashMap<>();

        // Tools Change Notification
        // 工具变更通知消费者
        List<Function<List<McpSchema.Tool>, Mono<Void>>> toolsChangeConsumersFinal = new ArrayList<>();
        // 添加默认的调试日志消费者
        toolsChangeConsumersFinal
                .add((notification) -> Mono.fromRunnable(() -> logger.debug("Tools changed: {}", notification)));
        // 添加用户自定义的消费者
        if (!Utils.isEmpty(features.toolsChangeConsumers())) {
            toolsChangeConsumersFinal.addAll(features.toolsChangeConsumers());
        }
        // 注册工具变更通知处理器
        notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_TOOLS_LIST_CHANGED,
                asyncToolsChangeNotificationHandler(toolsChangeConsumersFinal));

        // 资源变更通知消费者
        List<Function<List<McpSchema.Resource>, Mono<Void>>> resourcesChangeConsumersFinal = new ArrayList<>();
        resourcesChangeConsumersFinal
                .add((notification) -> Mono.fromRunnable(() -> logger.debug("Resources changed: {}", notification)));
        if (!Utils.isEmpty(features.resourcesChangeConsumers())) {
            resourcesChangeConsumersFinal.addAll(features.resourcesChangeConsumers());
        }
        // 注册资源变更通知处理器
        notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_RESOURCES_LIST_CHANGED,
                asyncResourcesChangeNotificationHandler(resourcesChangeConsumersFinal));

        // 提示词变更通知消费者
        List<Function<List<McpSchema.Prompt>, Mono<Void>>> promptsChangeConsumersFinal = new ArrayList<>();
        promptsChangeConsumersFinal
                .add((notification) -> Mono.fromRunnable(() -> logger.debug("Prompts changed: {}", notification)));
        if (!Utils.isEmpty(features.promptsChangeConsumers())) {
            promptsChangeConsumersFinal.addAll(features.promptsChangeConsumers());
        }
        // 注册提示词变更通知处理器
        notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_PROMPTS_LIST_CHANGED,
                asyncPromptsChangeNotificationHandler(promptsChangeConsumersFinal));

        // 日志记录变更通知消费者
        List<Function<LoggingMessageNotification, Mono<Void>>> loggingConsumersFinal = new ArrayList<>();
        loggingConsumersFinal.add((notification) -> Mono.fromRunnable(() -> logger.debug("Logging: {}", notification)));
        if (!Utils.isEmpty(features.loggingConsumers())) {
            loggingConsumersFinal.addAll(features.loggingConsumers());
        }
        // 注册日志记录变更通知处理器
        notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_MESSAGE,
                asyncLoggingNotificationHandler(loggingConsumersFinal));

        // 创建客户端会话
        this.mcpSession = new McpClientSession(requestTimeout, transport, requestHandlers, notificationHandlers);
    }

    /**
     * 立刻关闭客户端连接
     */
    public void close() {
        this.mcpSession.close();
    }

    /**
     * 优雅关闭客户端连接
     *
     * @return Mono,关闭后完成
     */
    public Mono<Void> closeGracefully() {
        return this.mcpSession.closeGracefully();
    }

    /**
     * 初始化阶段
     *
     * @return Mono,初始化后完成
     */
    public Mono<McpSchema.InitializeResult> initialize() {

        String latestVersion = this.protocolVersions.get(this.protocolVersions.size() - 1);
        // 构建初始化请求
        McpSchema.InitializeRequest initializeRequest = new McpSchema.InitializeRequest(// @formatter:off
                latestVersion,
                this.clientCapabilities,
                this.clientInfo); // @formatter:on

        // 向服务端发送初始化请求
        Mono<McpSchema.InitializeResult> result = this.mcpSession.sendRequest(McpSchema.METHOD_INITIALIZE,
                initializeRequest, new TypeReference<McpSchema.InitializeResult>() {
                });

        return result.flatMap(initializeResult -> {
            // 存放服务端配置信息
            this.serverCapabilities = initializeResult.capabilities();
            this.serverInstructions = initializeResult.instructions();
            this.serverInfo = initializeResult.serverInfo();

            logger.info("Server response with Protocol: {}, Capabilities: {}, Info: {} and Instructions {}",
                    initializeResult.protocolVersion(), initializeResult.capabilities(), initializeResult.serverInfo(),
                    initializeResult.instructions());

            if (!this.protocolVersions.contains(initializeResult.protocolVersion())) {
                // 客户端不支持服务端协议版本,返回错误信息
                return Mono.error(new McpError(
                        "Unsupported protocol version from the server: " + initializeResult.protocolVersion()));
            }

            // 向服务端发送通知,告知初始化完成
            return this.mcpSession.sendNotification(McpSchema.METHOD_NOTIFICATION_INITIALIZED, null).doOnSuccess(v -> {
                // 标记初始化完成
                this.initialized.set(true);
                this.initializedSink.tryEmitValue(initializeResult);
            }).thenReturn(initializeResult);
        });
    }

    /**
     * 在执行操作之前,确保客户端已经初始化完成
     *
     * @param actionName 执行的操作名
     * @param operation 具体的操作
     * @return Mono,操作执行后完成
     */
    private <T> Mono<T> withInitializationCheck(String actionName,
                                                Function<McpSchema.InitializeResult, Mono<T>> operation) {
        return this.initializedSink.asMono()
                .timeout(this.initializationTimeout)
                .onErrorResume(TimeoutException.class,
                        ex -> Mono.error(new McpError("Client must be initialized before " + actionName)))
                .flatMap(operation);
    }

    /**
     * 向服务端发送 ping
     *
     * @return Mono,服务器响应后完成
     */
    public Mono<Object> ping() {
        return this.withInitializationCheck("pinging the server", initializedResult -> this.mcpSession
                .sendRequest(McpSchema.METHOD_PING, null, new TypeReference<Object>() {
                }));
    }

    /**
     * 向服务端调用指定的工具
     *
     * @param callToolRequest 请求,包括工具名、参数
     * @return Mono,服务端响应后完成
     */
    public Mono<McpSchema.CallToolResult> callTool(McpSchema.CallToolRequest callToolRequest) {
        return this.withInitializationCheck("calling tools", initializedResult -> {
            if (this.serverCapabilities.tools() == null) {
                return Mono.error(new McpError("Server does not provide tools capability"));
            }
            // 向服务端发送请求调用工具
            return this.mcpSession.sendRequest(McpSchema.METHOD_TOOLS_CALL, callToolRequest, CALL_TOOL_RESULT_TYPE_REF);
        });
    }

    /**
     * 检索服务端提供的所有工具列表
     *
     * @return Mono,服务端响应后完成
     */
    public Mono<McpSchema.ListToolsResult> listTools() {
        return this.listTools(null);
    }

    /**
     * 检索服务端提供的所有工具列表
     *
     * @param cursor 分页游标(可选)
     * @return Mono,服务端响应后完成
     */
    public Mono<McpSchema.ListToolsResult> listTools(String cursor) {
        return this.withInitializationCheck("listing tools", initializedResult -> {
            if (this.serverCapabilities.tools() == null) {
                return Mono.error(new McpError("Server does not provide tools capability"));
            }
            // 向服务端发送请求获取工具列表
            return this.mcpSession.sendRequest(McpSchema.METHOD_TOOLS_LIST, new McpSchema.PaginatedRequest(cursor),
                    LIST_TOOLS_RESULT_TYPE_REF);
        });
    }

    /**
     * 构建工具变更通知处理器
     * @param toolsChangeConsumers 消费者
     * @return 处理器
     */
    private NotificationHandler asyncToolsChangeNotificationHandler(
            List<Function<List<McpSchema.Tool>, Mono<Void>>> toolsChangeConsumers) {
        return params -> this.listTools()
                // 将工具广播给所有消费者,并执行每个消费者的逻辑
                .flatMap(listToolsResult -> Flux.fromIterable(toolsChangeConsumers)
                        .flatMap(consumer -> consumer.apply(listToolsResult.tools()))
                        .onErrorResume(error -> {
                            logger.error("Error handling tools list change notification", error);
                            return Mono.empty();
                        })
                        .then());
    }

    // 其他方法省略...
}
Java 复制代码
/**
 * MCP 同步客户端,封装了 MCPAsyncClient 来提供阻塞操作的 API
 */
public class McpSyncClient implements AutoCloseable {

    // 代理,MCP 异步客户端
    private final McpAsyncClient delegate;

    /**
     * MCP 同步客户端构造方法
     *
     * @param delegate 代理,实际为 MCP 异步客户端
     */
    McpSyncClient(McpAsyncClient delegate) {
        Assert.notNull(delegate, "The delegate can not be null");
        this.delegate = delegate;
    }

    // 方法与异步客户端类似,只是内部逻辑都交由异步客户端来完成,且为阻塞过程

    @Override
    public void close() {
        this.delegate.close();
    }

    public boolean closeGracefully() {
        try {
            this.delegate.closeGracefully().block(Duration.ofMillis(DEFAULT_CLOSE_TIMEOUT_MS));
        }
        catch (RuntimeException e) {
            logger.warn("Client didn't close within timeout of {} ms.", DEFAULT_CLOSE_TIMEOUT_MS, e);
            return false;
        }
        return true;
    }

    public McpSchema.InitializeResult initialize() {
        return this.delegate.initialize().block();
    }

    public Object ping() {
        return this.delegate.ping().block();
    }

    public McpSchema.CallToolResult callTool(McpSchema.CallToolRequest callToolRequest) {
        return this.delegate.callTool(callToolRequest).block();
    }

    public McpSchema.ListToolsResult listTools() {
        return this.delegate.listTools().block();
    }
}

3.1.3 会话层

MCP 客户端会话层涉及到的类包括 McpSessionMcpClientSession,如下图所示:

会话层是 MCP 架构中负责消息收发和事件处理的核心部分,它将客户端与底层传输通道解耦,使上层逻辑无需关心具体通信细节。其通过 McpSession 接口定义统一操作规范,并在 McpClientSession 提供了具体实现。其核心方法包括:

  • sendRequest():向服务端发送请求消息并等待响应,返回 Mono 表示支持异步处理。
  • sendNotification():向服务端发送无响应的通知消息,返回 Mono 表示支持异步处理。
  • close():立即关闭客户端连接和会话资源。
  • closeGracefully():执行优雅关闭操作,确保未完成的请求正常结束,返回 Mono 表示支持异步处理。

McpClientSession 实现了上述的方法,其内部核心的成员变量包括:

  • requestTimeout:请求超时时间,用于控制请求等待时长,避免长时间阻塞。
  • transport:传输层对象,屏蔽了实际的传输协议实现。
  • pendingResponses:并发哈希表,保存所有待处理的请求响应,通过 MonoSink 与异步流绑定,实现请求-响应的异步匹配。
  • requestHandlers/notification:分别表示请求处理器和通知处理器,用于动态处理服务端请求或通知,当服务端向客户端发送请求或通知时,则调用对应的处理器进行处理。
  • connection:表示底层连接的生命周期管理对象,用于控制会话的启动与关闭。

核心的源码及注释如下所示:

Java 复制代码
/**
 * MCP 会话,用户处理客户端与服务端之间的通信
 * 提供了消息交互和会话生命周期管理的方法
 */
public interface McpSession {

    /**
     * 向服务端发送请求并返回响应结果
     *
     * @param method 调用的方法名
     * @param requestParams 请求参数
     * @param typeRef 反序列化响应结果所需的类型
     * @return Mono,服务端响应后完成
     */
    <T> Mono<T> sendRequest(String method, Object requestParams, TypeReference<T> typeRef);

    /**
     * 向服务端发送不带参数的通知
     *
     * @param method 方法名
     * @return Mono,通知发送后完成
     */
    default Mono<Void> sendNotification(String method) {
        return sendNotification(method, null);
    }

    /**
     * 向服务端发送带参数的通知
     *
     * @param method 通知方法名
     * @param params 方法参数
     * @return Mono,通知发送后完成
     */
    Mono<Void> sendNotification(String method, Object params);

    /**
     * 优雅关闭客户端连接
     *
     * @return Mono,关闭后完成
     */
    Mono<Void> closeGracefully();

    /**
     * 立刻关闭客户端连接
     */
    void close();
}
Java 复制代码
/**
 * MCP 客户端会话实现类,用于管理客户端和服务端之间的双向 JSON-RPC 通信
 */
public class McpClientSession implements McpSession {

    // 请求超时时间
    private final Duration requestTimeout;

    // 传输层对象
    private final McpClientTransport transport;

    // 待处理的请求响应
    private final ConcurrentHashMap<Object, MonoSink<McpSchema.JSONRPCResponse>> pendingResponses = new ConcurrentHashMap<>();

    // 请求处理器
    private final ConcurrentHashMap<String, RequestHandler<?>> requestHandlers = new ConcurrentHashMap<>();

    // 通知处理器
    private final ConcurrentHashMap<String, NotificationHandler> notificationHandlers = new ConcurrentHashMap<>();

    // 请求 id 的会话前缀
    private final String sessionPrefix = UUID.randomUUID().toString().substring(0, 8);

    // 用于生成唯一请求 id 的原子计数器
    private final AtomicLong requestCounter = new AtomicLong(0);

    // 底层连接对象
    private final Disposable connection;

    /**
     * 创建客户端会话对象
     *
     * @param requestTimeout 请求超时时间
     * @param transport 传输层对象
     * @param requestHandlers 请求处理器
     * @param notificationHandlers 通知处理器
     */
    public McpClientSession(Duration requestTimeout, McpClientTransport transport,
                            Map<String, RequestHandler<?>> requestHandlers, Map<String, NotificationHandler> notificationHandlers) {

        Assert.notNull(requestTimeout, "The requestTimeout can not be null");
        Assert.notNull(transport, "The transport can not be null");
        Assert.notNull(requestHandlers, "The requestHandlers can not be null");
        Assert.notNull(notificationHandlers, "The notificationHandlers can not be null");

        this.requestTimeout = requestTimeout;
        this.transport = transport;
        this.requestHandlers.putAll(requestHandlers);
        this.notificationHandlers.putAll(notificationHandlers);

        // 使用传输层对象建立连接
        this.connection = this.transport.connect(mono -> mono.doOnNext(this::handle)).subscribe();
    }

    /**
     * 处理接收到的 JSON-RPC 消息
     *
     * @param message JSON-RPC 消息
     */
    private void handle(McpSchema.JSONRPCMessage message) {
        if (message instanceof McpSchema.JSONRPCResponse response) {
            // 处理响应消息
            logger.debug("Received Response: {}", response);
            // 根据响应 id 取出对应的 MonoSink(等待结果的响应对象)
            var sink = pendingResponses.remove(response.id());
            if (sink == null) {
                logger.warn("Unexpected response for unknown id {}", response.id());
            }
            else {
                // 将响应结果推送给订阅者
                sink.success(response);
            }
        }
        else if (message instanceof McpSchema.JSONRPCRequest request) {
            // 处理请求消息
            logger.debug("Received request: {}", request);
            handleIncomingRequest(request).onErrorResume(error -> {
                // 处理请求时发生错误,则构造 JSON-RPC 错误响应发送给服务端
                var errorResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null,
                        new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR,
                                error.getMessage(), null));
                return this.transport.sendMessage(errorResponse).then(Mono.empty());
            }).flatMap(this.transport::sendMessage).subscribe();
        }
        else if (message instanceof McpSchema.JSONRPCNotification notification) {
            // 处理通知消息
            logger.debug("Received notification: {}", notification);
            handleIncomingNotification(notification)
                    .doOnError(error -> logger.error("Error handling notification: {}", error.getMessage()))
                    .subscribe();
        }
        else {
            logger.warn("Received unknown message type: {}", message);
        }
    }

    /**
     * 处理 JSON-RPC 请求消息,将其路由到对应的处理器
     *
     * @param request JSON-RPC 请求
     * @return Mono,处理后完成
     */
    private Mono<McpSchema.JSONRPCResponse> handleIncomingRequest(McpSchema.JSONRPCRequest request) {
        return Mono.defer(() -> {
            // 根据方法名获取处理器
            var handler = this.requestHandlers.get(request.method());
            if (handler == null) {
                // 不存在处理器时,返回 JSON-RPC 错误消息
                MethodNotFoundError error = getMethodNotFoundError(request.method());
                return Mono.just(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null,
                        new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.METHOD_NOT_FOUND,
                                error.message(), error.data())));
            }
            // 调用处理器,并将结果封装成 JSON-RPC 消息
            return handler.handle(request.params())
                    .map(result -> new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), result, null))
                    .onErrorResume(error -> Mono.just(new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(),
                            null, new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR,
                            error.getMessage(), null))));
        });
    }

    /**
     * 表示方法未找到的错误数据结构
     *
     * @param method 方法名
     * @param message 异常信息
     * @param data 附加数据
     */
    record MethodNotFoundError(String method, String message, Object data) {
    }

    /**
     * 根据方法名返回对应的 MethodNotFoundError 对象
     *
     * @param method 方法名
     * @return MethodNotFoundError 对象
     */
    private MethodNotFoundError getMethodNotFoundError(String method) {
        switch (method) {
            case McpSchema.METHOD_ROOTS_LIST:
                return new MethodNotFoundError(method, "Roots not supported",
                        Map.of("reason", "Client does not have roots capability"));
            default:
                return new MethodNotFoundError(method, "Method not found: " + method, null);
        }
    }

    /**
     * 处理 JSON-RPC 通知消息,将其路由到对应的处理器
     *
     * @param notification JSON-RPC 融资股
     * @return Mono,处理后完成
     */
    private Mono<Void> handleIncomingNotification(McpSchema.JSONRPCNotification notification) {
        return Mono.defer(() -> {
            // 根据方法名获取处理器
            var handler = notificationHandlers.get(notification.method());
            if (handler == null) {
                // 不存在处理器时直接返回
                logger.error("No handler registered for notification method: {}", notification.method());
                return Mono.empty();
            }
            // 调用处理器
            return handler.handle(notification.params());
        });
    }

    /**
     * 生成唯一的请求 id
     *
     * @return 请求 id
     */
    private String generateRequestId() {
        return this.sessionPrefix + "-" + this.requestCounter.getAndIncrement();
    }

    /**
     * 发送 JSON-RPC 请求并返回响应结果
     *
     * @param method 调用的方法名
     * @param requestParams 请求参数
     * @param typeRef 响应反序列化所需的类型
     * @return Mono,包含响应结果
     */
    @Override
    public <T> Mono<T> sendRequest(String method, Object requestParams, TypeReference<T> typeRef) {
        // 生成请求 id
        String requestId = this.generateRequestId();

        return Mono.deferContextual(ctx -> Mono.<McpSchema.JSONRPCResponse>create(sink -> {
            // 缓存当前请求
            this.pendingResponses.put(requestId, sink);
            // 构建 JSON-RPC 请求对象
            McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, method,
                    requestId, requestParams);
            // 发送消息
            this.transport.sendMessage(jsonrpcRequest)
                    .contextWrite(ctx)
                    .subscribe(v -> {
                    }, error -> {
                        this.pendingResponses.remove(requestId);
                        sink.error(error);
                    });
        })).timeout(this.requestTimeout).handle((jsonRpcResponse, sink) -> {
            // 处理响应
            if (jsonRpcResponse.error() != null) {
                // 出现异常
                logger.error("Error handling request: {}", jsonRpcResponse.error());
                sink.error(new McpError(jsonRpcResponse.error()));
            }
            else {
                if (typeRef.getType().equals(Void.class)) {
                    // 返回结果类型为空时直接结束
                    sink.complete();
                }
                else {
                    // 返回结果类型不为空时将结果转换为 typeRef 类型对象并返回
                    sink.next(this.transport.unmarshalFrom(jsonRpcResponse.result(), typeRef));
                }
            }
        });
    }

    /**
     * 发送 JSON-RPC 通知
     *
     * @param method 通知方法名
     * @param params 方法参数
     * @return Mono,发送后完成
     */
    @Override
    public Mono<Void> sendNotification(String method, Object params) {
        // 构建 JSON-RPC 消息
        McpSchema.JSONRPCNotification jsonrpcNotification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION,
                method, params);
        // 发送消息
        return this.transport.sendMessage(jsonrpcNotification);
    }

    /**
     * 优雅关闭客户端连接
     *
     * @return Mono,关闭后完成
     */
    @Override
    public Mono<Void> closeGracefully() {
        return Mono.defer(() -> {
            this.connection.dispose();
            return transport.closeGracefully();
        });
    }

    /**
     * 立刻关闭客户端连接
     */
    @Override
    public void close() {
        this.connection.dispose();
        transport.close();
    }

}

3.1.4 传输层

MCP 客户端传输层涉及到的类包括:McpTransportMcpClientTransportHttpClientSseClientTransportStdioClientTransport 等,其中 HttpClientSseClientTransportStdioClientTransportMcpClientTransport 实现类,分别对应 SSE 和 Stdio 的具体实现,这里仅列出两种,实际上不同的传输协议都会有对应的实现类。其类图如下所示:

传输层承担着所有消息的实际发送与接收职责,无论上层如何组织会话或封装客户端逻辑,最终都必须通过这一层将 JSON-RPC 消息序列化、发送并接收响应。核心接口是 McpTransport 和其子接口 McpClientTransport

  • McpTransport 接口定义了基础通信能力:
    • sendMessage():向服务端异步发送 JSON-RPC 消息。
    • unmarshalFrom():将数据转化为指定类型的对象。
    • close():立刻关闭传输连接。
    • closeGracefully():优雅关闭传输连接,将阻止新的消息发送,但允许正在进行的操作完成。
  • McpClientTransport 则扩展了客户端的连接行为:
    • connect():建立与服务端的连接,同时会将消息流与客户端的处理逻辑绑定起来。

核心的源码及注释如下所示(实现类中仅给出 HttpClientSseClientTransport 的具体实现,其他的实现类是类似的):

Java 复制代码
/**
 * MCP 异步传输层
 */
public interface McpTransport {

    /**
     * 立刻关闭客户端连接
     */
    default void close() {
        this.closeGracefully().subscribe();
    }

    /**
     * 优雅关闭客户端连接
     *
     * @return Mono,关闭后完成
     */
    Mono<Void> closeGracefully();

    /**
     * 异步发送 JSON-RPC 消息
     *
     * @param message JSON-RPC 消息
     * @return Mono,发送后完成
     */
    Mono<Void> sendMessage(JSONRPCMessage message);

    /**
     * 将数据转换为指定类型的对象
     *
     * @param data 数据
     * @param typeRef 指定类型
     * @return 指定类型的对象
     */
    <T> T unmarshalFrom(Object data, TypeReference<T> typeRef);

}
Java 复制代码
/**
 * MCP 客户端传输层
 */
public interface McpClientTransport extends McpTransport {

    /**
     * 与服务端建立连接
     *
     * @param handler 处理器,用于处理从服务端接收到的消息
     * @return Mono,连接建立时完成
     */
    Mono<Void> connect(Function<Mono<McpSchema.JSONRPCMessage>, Mono<McpSchema.JSONRPCMessage>> handler);

}
Java 复制代码
/**
 * SSE 传输层实现类
 */
public class HttpClientSseClientTransport implements McpClientTransport {

    private static final Logger logger = LoggerFactory.getLogger(HttpClientSseClientTransport.class);

    // 消息事件类型
    private static final String MESSAGE_EVENT_TYPE = "message";

    // 端点事件类型
    private static final String ENDPOINT_EVENT_TYPE = "endpoint";

    // SSE 端点默认值
    private static final String DEFAULT_SSE_ENDPOINT = "/sse";

    // MCP 服务端 uri
    private final URI baseUri;

    // SSE 端点
    private final String sseEndpoint;

    // SSE 客户端,用于处理服务端发送的事件
    private final FlowSseClient sseClient;

    // HTTP 客户端,用于向服务端发送消息,使用 HTTP/POST 方法
    private final HttpClient httpClient;

    // HTTP 请求构建器,用于构建向服务端发送消息的请求
    private final HttpRequest.Builder requestBuilder;

    // JSON 对象映射器,用于消息序列化/反序列化
    protected ObjectMapper objectMapper;

    // 标志传输是否处于关闭状态
    private volatile boolean isClosing = false;

    // 协调端点发现的锁
    private final CountDownLatch closeLatch = new CountDownLatch(1);

    // 消息端点
    private final AtomicReference<String> messageEndpoint = new AtomicReference<>();

    // SSE 连接
    private final AtomicReference<CompletableFuture<Void>> connectionFuture = new AtomicReference<>();

    /**
     * 创建传输层对象
     *
     * @param httpClient HTTP 客户端
     * @param requestBuilder HTTP 请求构建器
     * @param baseUri MCP 服务端 uri
     * @param sseEndpoint SSE 端点
     * @param objectMapper JSON 对象映射器
     */
    HttpClientSseClientTransport(HttpClient httpClient, HttpRequest.Builder requestBuilder, String baseUri,
                                 String sseEndpoint, ObjectMapper objectMapper) {
        Assert.notNull(objectMapper, "ObjectMapper must not be null");
        Assert.hasText(baseUri, "baseUri must not be empty");
        Assert.hasText(sseEndpoint, "sseEndpoint must not be empty");
        Assert.notNull(httpClient, "httpClient must not be null");
        Assert.notNull(requestBuilder, "requestBuilder must not be null");
        this.baseUri = URI.create(baseUri);
        this.sseEndpoint = sseEndpoint;
        this.objectMapper = objectMapper;
        this.httpClient = httpClient;
        this.requestBuilder = requestBuilder;

        this.sseClient = new FlowSseClient(this.httpClient, requestBuilder);
    }

    /**
     * 与服务端建立 SSE 连接并设置消息处理
     *
     * @param handler 处理接收到的 JSON-RPC 消息
     * @return Mono,当连接建立时完成
     */
    @Override
    public Mono<Void> connect(Function<Mono<JSONRPCMessage>, Mono<JSONRPCMessage>> handler) {
        CompletableFuture<Void> future = new CompletableFuture<>();
        connectionFuture.set(future);

        // 拼接 SSE 服务端的完整 URI(baseUri + sseEndpoint)
        URI clientUri = Utils.resolveUri(this.baseUri, this.sseEndpoint);
        // 发起订阅,监听服务端的 SSE 事件
        sseClient.subscribe(clientUri.toString(), new FlowSseClient.SseEventHandler() {
            @Override
            public void onEvent(SseEvent event) {
                if (isClosing) {
                    // 若传输关闭则直接退出
                    return;
                }

                try {
                    if (ENDPOINT_EVENT_TYPE.equals(event.type())) {
                        // endpoint 事件,接收服务端返回的消息发送端点地址
                        String endpoint = event.data();
                        messageEndpoint.set(endpoint);
                        closeLatch.countDown();
                        future.complete(null);
                    }
                    else if (MESSAGE_EVENT_TYPE.equals(event.type())) {
                        // message 事件,接收服务端推送的 JSON-RPC 消息
                        // 反序列化数据为 JSON-RPC 格式数据
                        JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(objectMapper, event.data());
                        // 调用处理器来处理服务端返回的消息
                        handler.apply(Mono.just(message)).subscribe();
                    }
                    else {
                        logger.error("Received unrecognized SSE event type: {}", event.type());
                    }
                }
                catch (IOException e) {
                    logger.error("Error processing SSE event", e);
                    future.completeExceptionally(e);
                }
            }

            @Override
            public void onError(Throwable error) {
                if (!isClosing) {
                    logger.error("SSE connection error", error);
                    future.completeExceptionally(error);
                }
            }
        });

        return Mono.fromFuture(future);
    }

    /**
     * 向服务端发送 JSON-RPC 消息
     *
     * @param message 发送的 JSON-RPC 消息
     * @return Mono,当消息发送成功时完成
     */
    @Override
    public Mono<Void> sendMessage(JSONRPCMessage message) {
        if (isClosing) {
            return Mono.empty();
        }

        try {
            if (!closeLatch.await(10, TimeUnit.SECONDS)) {
                // 确保连接已经建立
                return Mono.error(new McpError("Failed to wait for the message endpoint"));
            }
        }
        catch (InterruptedException e) {
            return Mono.error(new McpError("Failed to wait for the message endpoint"));
        }

        // 获取发送消息的端点
        String endpoint = messageEndpoint.get();
        if (endpoint == null) {
            return Mono.error(new McpError("No message endpoint available"));
        }

        try {
            // 序列化消息,将 JSON-RPC 消息转化为字符串
            String jsonText = this.objectMapper.writeValueAsString(message);
            // 拼接 URI(baseUri + messageEndpoint)
            URI requestUri = Utils.resolveUri(baseUri, endpoint);
            // 构建 POST 请求
            HttpRequest request = this.requestBuilder.uri(requestUri)
                    .POST(HttpRequest.BodyPublishers.ofString(jsonText))
                    .build();

            return Mono.fromFuture(
                    // 异步发送 HTTP 请求
                    httpClient.sendAsync(request, HttpResponse.BodyHandlers.discarding()).thenAccept(response -> {
                        if (response.statusCode() != 200 && response.statusCode() != 201 && response.statusCode() != 202
                                && response.statusCode() != 206) {
                            logger.error("Error sending message: {}", response.statusCode());
                        }
                    }));
        }
        catch (IOException e) {
            if (!isClosing) {
                return Mono.error(new RuntimeException("Failed to serialize message", e));
            }
            return Mono.empty();
        }
    }

    /**
     * 优雅地关闭传输连接,将阻止新的消息发送,并允许正在进行的操作完成
     *
     * @return Mono,当关闭成功时完成
     */
    @Override
    public Mono<Void> closeGracefully() {
        return Mono.fromRunnable(() -> {
            // 设置关闭标志
            isClosing = true;
            CompletableFuture<Void> future = connectionFuture.get();
            if (future != null && !future.isDone()) {
                // 关闭连接
                future.cancel(true);
            }
        });
    }

    /**
     * 使用配置的对象映射器将数据解析为指定类型
     *
     * @param data 解析的数据
     * @param typeRef 转换的目标类型
     * @return 解析后的对象
     */
    @Override
    public <T> T unmarshalFrom(Object data, TypeReference<T> typeRef) {
        return this.objectMapper.convertValue(data, typeRef);
    }
}

3.2 服务端

3.2.1 入口层

MCP 服务端入口层涉及到的类包括 McpServerMcpServer.AsyncSpecification (内部类)、McpClient.SyncSpecification(内部类),如下图所示:

与客户端类似,入口层也是创建 MCP 服务端的统一入口,通过静态方法 sync()async() 提供了同步和异步两种模式的构建方式,分别返回 SyncSpecificationAyncSpecification 这两个内部构建器类,用于配置客户端实例的各项参数并最终生成对应的 McpSyncServer McpAsyncServer

并且,AyncSpecificationSyncSpecification 的设计也十分相似,都封装了配置信息,且它们内部的方法(如 serverInfo()capabilities())都只是对构建器内成员变量和的简单赋值。而不同的是,异步模式在处理服务端的变更通知时,是基于 Reactor 的 Mono 进行响应式调用的,而同步模式则使用传统的 Consumer 回调方式。

核心的参数描述如下:

  • transportProvider:会话管理层实现对象。
  • objectMappe:对象映射器,用于序列化和反序列化 JSON 消息。
  • serverInfo:服务端基本信息,包含服务端名字和版本,
  • capabilities:服务端能力配置,包含补全、日志、工具等配置。
  • tools:服务端可调用的工具列表。
  • requestTimeout:请求超时时间,默认 10s。

核心的源码及注释如下所示:

Java 复制代码
/**
 * 创建 MCP 服务端的工厂类
 */
public interface McpServer {

    /**
     * 构建一个提供阻塞操作的同步 MCP 服务端
     *
     * @param transportProvider MCP 传输层实现
     * @return 同步 MCP 服务端构建器
     */
    static SyncSpecification sync(McpServerTransportProvider transportProvider) {
        return new SyncSpecification(transportProvider);
    }

    /**
     * 提供一个非阻塞操作的异步 MCP 服务端
     *
     * @param transportProvider MCP 会话管理对象
     * @return 异步 MCP 服务端构建器
     */
    static AsyncSpecification async(McpServerTransportProvider transportProvider) {
        return new AsyncSpecification(transportProvider);
    }

    /**
     * 异步 MCP 服务端构建器
     */
    class AsyncSpecification {

        // 默认服务端基本信息
        private static final McpSchema.Implementation DEFAULT_SERVER_INFO = new McpSchema.Implementation("mcp-server",
                "1.0.0");

        // 会话管理对象
        private final McpServerTransportProvider transportProvider;

        // JSON 对象映射器,用于消息序列化/反序列化
        private ObjectMapper objectMapper;

        // 服务端基本信息
        private McpSchema.Implementation serverInfo = DEFAULT_SERVER_INFO;

        // 服务端支持的功能
        private McpSchema.ServerCapabilities serverCapabilities;

        // 服务端可调用的工具列表
        private final List<McpServerFeatures.AsyncToolSpecification> tools = new ArrayList<>();

        // 请求超时时间
        private Duration requestTimeout = Duration.ofSeconds(10);

        // 其他属性省略...

        private AsyncSpecification(McpServerTransportProvider transportProvider) {
            Assert.notNull(transportProvider, "Transport provider must not be null");
            this.transportProvider = transportProvider;
        }

        /**
         * 设置对象映射器
         */
        public AsyncSpecification objectMapper(ObjectMapper objectMapper) {
            Assert.notNull(objectMapper, "ObjectMapper must not be null");
            this.objectMapper = objectMapper;
            return this;
        }

        /**
         * 设置服务端基本信息
         */
        public AsyncSpecification serverInfo(McpSchema.Implementation serverInfo) {
            Assert.notNull(serverInfo, "Server info must not be null");
            this.serverInfo = serverInfo;
            return this;
        }

        /**
         * 设置服务端支持的功能
         */
        public AsyncSpecification capabilities(McpSchema.ServerCapabilities serverCapabilities) {
            Assert.notNull(serverCapabilities, "Server capabilities must not be null");
            this.serverCapabilities = serverCapabilities;
            return this;
        }

        /**
         * 添加工具
         */
        public AsyncSpecification tools(List<McpServerFeatures.AsyncToolSpecification> toolSpecifications) {
            Assert.notNull(toolSpecifications, "Tool handlers list must not be null");
            this.tools.addAll(toolSpecifications);
            return this;
        }

        /**
         * 设置超时时间
         */
        public AsyncSpecification requestTimeout(Duration requestTimeout) {
            Assert.notNull(requestTimeout, "Request timeout must not be null");
            this.requestTimeout = requestTimeout;
            return this;
        }

        /**
         * 构建异步 MCP 服务端
         */
        public McpAsyncServer build() {
            var features = new McpServerFeatures.Async(this.serverInfo, this.serverCapabilities, this.tools,
                    this.resources, this.resourceTemplates, this.prompts, this.completions, this.rootsChangeHandlers,
                    this.instructions);
            var mapper = this.objectMapper != null ? this.objectMapper : new ObjectMapper();
            return new McpAsyncServer(this.transportProvider, mapper, features, this.requestTimeout,
                    this.uriTemplateManagerFactory);
        }

        // 其他方法省略...
    }

    /**
     * 同步 MCP 服务端构建器
     */
    class SyncSpecification {
        // 异步构建器的属性和方法也是类似的

        private static final McpSchema.Implementation DEFAULT_SERVER_INFO = new McpSchema.Implementation("mcp-server",
                "1.0.0");

        private final McpServerTransportProvider transportProvider;

        private ObjectMapper objectMapper;

        private McpSchema.Implementation serverInfo = DEFAULT_SERVER_INFO;

        private McpSchema.ServerCapabilities serverCapabilities;

        private final List<McpServerFeatures.SyncToolSpecification> tools = new ArrayList<>();

        private Duration requestTimeout = Duration.ofSeconds(10);

        // 其他属性省略...

        private SyncSpecification(McpServerTransportProvider transportProvider) {
            Assert.notNull(transportProvider, "Transport provider must not be null");
            this.transportProvider = transportProvider;
        }

        public SyncSpecification objectMapper(ObjectMapper objectMapper) {
            Assert.notNull(objectMapper, "ObjectMapper must not be null");
            this.objectMapper = objectMapper;
            return this;
        }

        public SyncSpecification serverInfo(McpSchema.Implementation serverInfo) {
            Assert.notNull(serverInfo, "Server info must not be null");
            this.serverInfo = serverInfo;
            return this;
        }

        public SyncSpecification capabilities(McpSchema.ServerCapabilities serverCapabilities) {
            Assert.notNull(serverCapabilities, "Server capabilities must not be null");
            this.serverCapabilities = serverCapabilities;
            return this;
        }

        public SyncSpecification tools(List<McpServerFeatures.SyncToolSpecification> toolSpecifications) {
            Assert.notNull(toolSpecifications, "Tool handlers list must not be null");
            this.tools.addAll(toolSpecifications);
            return this;
        }

        public SyncSpecification requestTimeout(Duration requestTimeout) {
            Assert.notNull(requestTimeout, "Request timeout must not be null");
            this.requestTimeout = requestTimeout;
            return this;
        }

        public McpSyncServer build() {
            // 由于「同步」服务端依赖于「异步」服务端,因此这里会先创建「异步」服务端,再将其作为「同步」服务端的属性
            McpServerFeatures.Sync syncFeatures = new McpServerFeatures.Sync(this.serverInfo, this.serverCapabilities,
                    this.tools, this.resources, this.resourceTemplates, this.prompts, this.completions,
                    this.rootsChangeHandlers, this.instructions);
            McpServerFeatures.Async asyncFeatures = McpServerFeatures.Async.fromSync(syncFeatures);
            var mapper = this.objectMapper != null ? this.objectMapper : new ObjectMapper();
            var asyncServer = new McpAsyncServer(this.transportProvider, mapper, asyncFeatures, this.requestTimeout,
                    this.uriTemplateManagerFactory);

            return new McpSyncServer(asyncServer);
        }

        // 其他方法省略...
    }


}

3.2.2 运行层

MCP 服务端运行层涉及到的类包括 McpSyncServerMcpAsyncServer,如下图所示:

在调用入口层中构建器的 build() 方法后,就能构建出运行层的服务端对象。与客户端类似,同步服务端并没有独立实现实际的业务逻辑,而是通过组合的方式持有一个异步服务端实例,并将所有核心方法调用委托给它执行。其核心的方法包括:

  • addTool():注册一个可用的工具。
  • removeTool():注销工具。
  • notifyToolsListChanged():通知客户端可用工具列表已更改。
  • close():立即关闭服务端连接和会话资源。
  • closeGracefully():执行优雅关闭操作,确保未完成的请求正常结束。

核心的源码及注释如下所示:

Java 复制代码
/**
 * MCP 异步服务端
 */
public class McpAsyncServer {

    private static final Logger logger = LoggerFactory.getLogger(McpAsyncServer.class);

    // 会话管理层对象
    private final McpServerTransportProvider mcpTransportProvider;

    // 对象映射器
    private final ObjectMapper objectMapper;

    // 服务端能力配置
    private final McpSchema.ServerCapabilities serverCapabilities;

    // 服务端基本信息
    private final McpSchema.Implementation serverInfo;

    // 工具列表
    private final CopyOnWriteArrayList<McpServerFeatures.AsyncToolSpecification> tools = new CopyOnWriteArrayList<>();

    // 其他属性省略...

    /**
     * MCP 异步服务端构造方法
     *
     * @param mcpTransportProvider 会话管理层对象
     * @param objectMapper 对象映射器
     * @param features 服务端特征
     * @param requestTimeout 请求超时时间
     */
    McpAsyncServer(McpServerTransportProvider mcpTransportProvider, ObjectMapper objectMapper,
                   McpServerFeatures.Async features, Duration requestTimeout,
                   McpUriTemplateManagerFactory uriTemplateManagerFactory) {
        this.mcpTransportProvider = mcpTransportProvider;
        this.objectMapper = objectMapper;
        this.serverInfo = features.serverInfo();
        this.serverCapabilities = features.serverCapabilities();
        this.instructions = features.instructions();
        this.tools.addAll(features.tools());
        this.resources.putAll(features.resources());
        this.resourceTemplates.addAll(features.resourceTemplates());
        this.prompts.putAll(features.prompts());
        this.completions.putAll(features.completions());
        this.uriTemplateManagerFactory = uriTemplateManagerFactory;

        Map<String, McpServerSession.RequestHandler<?>> requestHandlers = new HashMap<>();

        // 初始化每个 MCP 方法对应的处理器

        // 添加 PING 方法处理器
        requestHandlers.put(McpSchema.METHOD_PING, (exchange, params) -> Mono.just(Map.of()));

        if (this.serverCapabilities.tools() != null) {
            // 添加列出工具列表处理器
            requestHandlers.put(McpSchema.METHOD_TOOLS_LIST, toolsListRequestHandler());
            // 添加工具调用处理器
            requestHandlers.put(McpSchema.METHOD_TOOLS_CALL, toolsCallRequestHandler());
        }

        // 添加资源处理器
        if (this.serverCapabilities.resources() != null) {
            requestHandlers.put(McpSchema.METHOD_RESOURCES_LIST, resourcesListRequestHandler());
            requestHandlers.put(McpSchema.METHOD_RESOURCES_READ, resourcesReadRequestHandler());
            requestHandlers.put(McpSchema.METHOD_RESOURCES_TEMPLATES_LIST, resourceTemplateListRequestHandler());
        }

        // 添加模板处理器
        if (this.serverCapabilities.prompts() != null) {
            requestHandlers.put(McpSchema.METHOD_PROMPT_LIST, promptsListRequestHandler());
            requestHandlers.put(McpSchema.METHOD_PROMPT_GET, promptsGetRequestHandler());
        }

        // 添加日志处理器
        if (this.serverCapabilities.logging() != null) {
            requestHandlers.put(McpSchema.METHOD_LOGGING_SET_LEVEL, setLoggerRequestHandler());
        }

        // 添加自动补全处理器
        if (this.serverCapabilities.completions() != null) {
            requestHandlers.put(McpSchema.METHOD_COMPLETION_COMPLETE, completionCompleteRequestHandler());
        }

        // 初始化每个通知方法对应的处理器
        Map<String, McpServerSession.NotificationHandler> notificationHandlers = new HashMap<>();

        // 添加初始化完成通知处理器
        notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_INITIALIZED, (exchange, params) -> Mono.empty());

        // 根列表变更消费者
        List<BiFunction<McpAsyncServerExchange, List<McpSchema.Root>, Mono<Void>>> rootsChangeConsumers = features
                .rootsChangeConsumers();

        if (Utils.isEmpty(rootsChangeConsumers)) {
            rootsChangeConsumers = List.of((exchange, roots) -> Mono.fromRunnable(() -> logger
                    .warn("Roots list changed notification, but no consumers provided. Roots list changed: {}", roots)));
        }

        // 添加根列表变更通知处理器
        notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_ROOTS_LIST_CHANGED,
                asyncRootsListChangedNotificationHandler(rootsChangeConsumers));

        // 设置会话工厂对象
        mcpTransportProvider.setSessionFactory(
                transport -> new McpServerSession(UUID.randomUUID().toString(), requestTimeout, transport,
                        this::asyncInitializeRequestHandler, Mono::empty, requestHandlers, notificationHandlers));
    }

    /**
     * 初始化处理器
     *
     * @param initializeRequest 初始化请求
     * @return Mono,初始化后完成
     */
    private Mono<McpSchema.InitializeResult> asyncInitializeRequestHandler(
            McpSchema.InitializeRequest initializeRequest) {
        return Mono.defer(() -> {
            logger.info("Client initialize request - Protocol: {}, Capabilities: {}, Info: {}",
                    initializeRequest.protocolVersion(), initializeRequest.capabilities(),
                    initializeRequest.clientInfo());

            // 需要返回的服务端协议版本,为当前支持的最高协议版本
            String serverProtocolVersion = this.protocolVersions.get(this.protocolVersions.size() - 1);

            if (this.protocolVersions.contains(initializeRequest.protocolVersion())) {
                // 如果服务端支持客户端请求时指定的协议版本,则响应相同的版本
                serverProtocolVersion = initializeRequest.protocolVersion();
            }
            else {
                logger.warn(
                        "Client requested unsupported protocol version: {}, so the server will suggest the {} version instead",
                        initializeRequest.protocolVersion(), serverProtocolVersion);
            }

            // 将服务端的能力配置、基本信息等返回给客户端
            return Mono.just(new McpSchema.InitializeResult(serverProtocolVersion, this.serverCapabilities,
                    this.serverInfo, this.instructions));
        });
    }

    /**
     * 优雅关闭服务端
     */
    public Mono<Void> closeGracefully() {
        return this.mcpTransportProvider.closeGracefully();
    }

    /**
     * Close the server immediately.
     */
    /**
     * 立即关闭服务端
     */
    public void close() {
        this.mcpTransportProvider.close();
    }

    /**
     * 添加新的工具
     *
     * @param toolSpecification 要添加的工具规范
     * @return Mono,向客户端发送通知后返回
     */
    public Mono<Void> addTool(McpServerFeatures.AsyncToolSpecification toolSpecification) {
        if (toolSpecification == null) {
            return Mono.error(new McpError("Tool specification must not be null"));
        }
        if (toolSpecification.tool() == null) {
            return Mono.error(new McpError("Tool must not be null"));
        }
        if (toolSpecification.call() == null) {
            return Mono.error(new McpError("Tool call handler must not be null"));
        }
        if (this.serverCapabilities.tools() == null) {
            return Mono.error(new McpError("Server must be configured with tool capabilities"));
        }

        return Mono.defer(() -> {
            // 检查是否存在重复名字的工具,如果存在直接返回错误信息
            if (this.tools.stream().anyMatch(th -> th.tool().name().equals(toolSpecification.tool().name()))) {
                return Mono
                        .error(new McpError("Tool with name '" + toolSpecification.tool().name() + "' already exists"));
            }

            this.tools.add(toolSpecification);
            logger.debug("Added tool handler: {}", toolSpecification.tool().name());

            if (this.serverCapabilities.tools().listChanged()) {
                // 如果开启通知配置,则通知客户端工具列表变更
                return notifyToolsListChanged();
            }
            return Mono.empty();
        });
    }

    /**
     * 移除工具
     *
     * @param toolName 移除的工具名
     * @return Mono,向客户端发送通知后完成
     */
    public Mono<Void> removeTool(String toolName) {
        if (toolName == null) {
            return Mono.error(new McpError("Tool name must not be null"));
        }
        if (this.serverCapabilities.tools() == null) {
            return Mono.error(new McpError("Server must be configured with tool capabilities"));
        }

        return Mono.defer(() -> {
            // 移除工具
            boolean removed = this.tools
                    .removeIf(toolSpecification -> toolSpecification.tool().name().equals(toolName));
            if (removed) {
                logger.debug("Removed tool handler: {}", toolName);
                if (this.serverCapabilities.tools().listChanged()) {
                    // 如果开启通知配置,则通知客户端工具列表变更
                    return notifyToolsListChanged();
                }
                return Mono.empty();
            }
            // 如果不存在对应名字的工具,即移除失败,则返回错误信息
            return Mono.error(new McpError("Tool with name '" + toolName + "' not found"));
        });
    }

    /**
     * 通知客户端可用工具列表已更改
     *
     * @return Mono,通知后完成
     */
    public Mono<Void> notifyToolsListChanged() {
        // 调用会话管理层对象来通知
        return this.mcpTransportProvider.notifyClients(McpSchema.METHOD_NOTIFICATION_TOOLS_LIST_CHANGED, null);
    }

    /**
     * 列出工具列表处理器
     *
     * @return 工具列表
     */
    private McpServerSession.RequestHandler<McpSchema.ListToolsResult> toolsListRequestHandler() {
        return (exchange, params) -> {
            List<Tool> tools = this.tools.stream().map(McpServerFeatures.AsyncToolSpecification::tool).toList();

            return Mono.just(new McpSchema.ListToolsResult(tools, null));
        };
    }

    /**
     * 工具调用处理器
     *
     * @return 工具调用结果
     */
    private McpServerSession.RequestHandler<CallToolResult> toolsCallRequestHandler() {
        return (exchange, params) -> {
            // 将请求参数转换为 CallToolRequest 对象
            McpSchema.CallToolRequest callToolRequest = objectMapper.convertValue(params,
                    new TypeReference<McpSchema.CallToolRequest>() {
                    });

            // 获取需要调用的工具
            Optional<McpServerFeatures.AsyncToolSpecification> toolSpecification = this.tools.stream()
                    .filter(tr -> callToolRequest.name().equals(tr.tool().name()))
                    .findAny();

            if (toolSpecification.isEmpty()) {
                // 如果没找到工具,则返回错误信息
                return Mono.error(new McpError("Tool not found: " + callToolRequest.name()));
            }

            return toolSpecification.map(tool -> tool.call().apply(exchange, callToolRequest.arguments()))
                    .orElse(Mono.error(new McpError("Tool not found: " + callToolRequest.name())));
        };
    }

    // 其他方法省略...

}
Java 复制代码
/**
 * MCP 同步服务端,封装了 MCPAsyncServer 来提供阻塞操作的 API
 */
public class McpSyncServer {

    // 异步服务端代理,实际的调用执行者
    private final McpAsyncServer asyncServer;

    /**
     * MCP 同步客户端构造方法
     *
     * @param asyncServer 代理,实际为 MCP 异步客户端
     */
    public McpSyncServer(McpAsyncServer asyncServer) {
        Assert.notNull(asyncServer, "Async server must not be null");
        this.asyncServer = asyncServer;
    }

    // 方法与异步服务端类似,只是内部逻辑都交由异步服务端来完成,且为阻塞过程

    public void addTool(McpServerFeatures.SyncToolSpecification toolHandler) {
        this.asyncServer.addTool(McpServerFeatures.AsyncToolSpecification.fromSync(toolHandler)).block();
    }

    public void removeTool(String toolName) {
        this.asyncServer.removeTool(toolName).block();
    }

    public void notifyToolsListChanged() {
        this.asyncServer.notifyToolsListChanged().block();
    }

    public void closeGracefully() {
        this.asyncServer.closeGracefully().block();
    }

    public void close() {
        this.asyncServer.close();
    }

}

3.2.3 会话管理层

MCP 服务端会话管理层涉及到的类包括 McpServerTransportProviderHttpServletSseServerTransportProviderStdioServerTransportProvider 等,其中 HttpServletSseServerTransportProviderStdioServerTransportProvider 为 McpServerTransportProvider 实现类,分别对应 SSE、Stdio 的具体实现,这里仅列出两种,实际上底层使用了不同的传输协议,会对应着不同的实现类。其类图如下图所示:

这一层是 MCP 服务端特有的部分,用于管理多个客户端会话。其中 McpServerTransportProvider 定义了相关的核心方法:

  • setSessionFactory():设置工厂对象,用于为新客户端创建会话的工厂对象。
  • notifyClients():向所有已连接的客户端发送通知。
  • close():立即关闭所有已连接客户端的传输并释放所有关联资源。
  • closeGracefully():优雅地关闭所有已连接客户端的传输并异步释放所有关联资源。

核心的源码及注释如下所示(实现类中仅给出 HttpServletSseServerTransportProvider 的具体实现,其他的实现类是类似的):

Java 复制代码
/**
 * 用于桥接服务端和 MCP 服务器传输层
 */
public interface McpServerTransportProvider {

    /**
     * 设置工厂对象,用于为新客户端创建会话的工厂对象
     *
     * @param sessionFactory 会话工厂
     */
    void setSessionFactory(McpServerSession.Factory sessionFactory);

    /**
     * 向所有已连接的客户端发送通知
     *
     * @param method 通知方法名
     * @param params 通知方法对应的参数
     * @return Mono,通知后完成
     */
    Mono<Void> notifyClients(String method, Object params);

    /**
     * 立即关闭所有已连接客户端的传输并释放所有关联资源
     */
    default void close() {
        this.closeGracefully().subscribe();
    }

    /**
     * 优雅地关闭所有已连接客户端的传输并异步释放所有关联资源
     *
     * @return Mono,当关闭成功时完成
     */
    Mono<Void> closeGracefully();

}
Java 复制代码
@WebServlet(asyncSupported = true)
public class HttpServletSseServerTransportProvider extends HttpServlet implements McpServerTransportProvider {

    private static final Logger logger = LoggerFactory.getLogger(HttpServletSseServerTransportProvider.class);

    public static final String UTF_8 = "UTF-8";

    public static final String APPLICATION_JSON = "application/json";

    public static final String FAILED_TO_SEND_ERROR_RESPONSE = "Failed to send error response: {}";

    // 默认的 SSE 连接端点
    public static final String DEFAULT_SSE_ENDPOINT = "/sse";

    // 消息事件类型
    public static final String MESSAGE_EVENT_TYPE = "message";

    // 端点事件类型
    public static final String ENDPOINT_EVENT_TYPE = "endpoint";

    public static final String DEFAULT_BASE_URL = "";

    // 对象映射器
    private final ObjectMapper objectMapper;

    // 服务端传输基本 url
    private final String baseUrl;

    // 消息端点,用于处理服务端发送的消息
    private final String messageEndpoint;

    // SSE 端点,用于处理 SSE 连接
    private final String sseEndpoint;

    // 活跃会话 map,key 为会话 id
    private final Map<String, McpServerSession> sessions = new ConcurrentHashMap<>();

    // 标记传输是否正在关闭
    private final AtomicBoolean isClosing = new AtomicBoolean(false);

    // 会话工厂,用于创建新的会话
    private McpServerSession.Factory sessionFactory;

    /**
     * 构造方法,使用默认的 baseUrl
     */
    public HttpServletSseServerTransportProvider(ObjectMapper objectMapper, String messageEndpoint,
                                                 String sseEndpoint) {
        this(objectMapper, DEFAULT_BASE_URL, messageEndpoint, sseEndpoint);
    }

    /**
     * 构造方法
     *
     * @param objectMapper 对象映射器
     * @param baseUrl 服务端基本 url
     * @param messageEndpoint 消息端点
     * @param sseEndpoint SSE 端点
     */
    public HttpServletSseServerTransportProvider(ObjectMapper objectMapper, String baseUrl, String messageEndpoint,
                                                 String sseEndpoint) {
        this.objectMapper = objectMapper;
        this.baseUrl = baseUrl;
        this.messageEndpoint = messageEndpoint;
        this.sseEndpoint = sseEndpoint;
    }

    /**
     * 设置工厂对象,用于为新客户端创建会话的工厂对象
     *
     * @param sessionFactory 会话工厂
     */
    @Override
    public void setSessionFactory(McpServerSession.Factory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    /**
     * 向所有已连接的客户端发送通知
     *
     * @param method 通知方法名
     * @param params 通知方法对应的参数
     * @return Mono,通知后完成
     */
    @Override
    public Mono<Void> notifyClients(String method, Object params) {
        if (sessions.isEmpty()) {
            // 无连接中的会话,直接返回
            logger.debug("No active sessions to broadcast message to");
            return Mono.empty();
        }

        logger.debug("Attempting to broadcast message to {} active sessions", sessions.size());

        return Flux.fromIterable(sessions.values())
                // 调用会话对象的方法来发送通知
                .flatMap(session -> session.sendNotification(method, params)
                        .doOnError(
                                e -> logger.error("Failed to send message to session {}: {}", session.getId(), e.getMessage()))
                        .onErrorComplete())
                .then();
    }

    /**
     * 处理 GET 请求来建立 SSE 连接
     * @param request HTTP Servlet 请求
     * @param response HTTP Servlet 响应
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 获取 uri
        String requestURI = request.getRequestURI();
        if (!requestURI.endsWith(sseEndpoint)) {
            // 只允许 SSE 端点连接请求
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        if (isClosing.get()) {
            // 连接正在被关闭时拒绝新的请求
            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Server is shutting down");
            return;
        }

        // 设置 SSE 响应头
        response.setContentType("text/event-stream");
        response.setCharacterEncoding(UTF_8);
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Connection", "keep-alive");
        response.setHeader("Access-Control-Allow-Origin", "*");

        // 生成唯一 sessionId
        String sessionId = UUID.randomUUID().toString();
        // 开启异步上下文
        AsyncContext asyncContext = request.startAsync();
        // 设置连接永不过期,即 SSE 连接可长时间保持
        asyncContext.setTimeout(0);

        PrintWriter writer = response.getWriter();

        // 将 sessionId、异步上下文、响应输出流封装进传输层对象
        HttpServletMcpSessionTransport sessionTransport = new HttpServletMcpSessionTransport(sessionId, asyncContext,
                writer);

        // 使用会话工厂创建一个新的 session 并注册进 sessions
        McpServerSession session = sessionFactory.create(sessionTransport);
        this.sessions.put(sessionId, session);

        // 向客户端发送 endpoint 事件,内容包含 baseUrl、消息端点、sessionId 组成的消息端点 url
        this.sendEvent(writer, ENDPOINT_EVENT_TYPE, this.baseUrl + this.messageEndpoint + "?sessionId=" + sessionId);
    }

    /**
     * 处理客户端的 POST 请求消息
     *
     * @param request HTTP Servlet 请求
     * @param response HTTP Servlet 响应
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        if (isClosing.get()) {
            // 连接正在被关闭时拒绝新的请求
            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Server is shutting down");
            return;
        }

        // 获取 uri
        String requestURI = request.getRequestURI();
        if (!requestURI.endsWith(messageEndpoint)) {
            // 只允许消息端点请求
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        //  从请求参数中获取 sessionId
        String sessionId = request.getParameter("sessionId");
        if (sessionId == null) {
            // 如果 sessionId 为空,返回异常信息
            response.setContentType(APPLICATION_JSON);
            response.setCharacterEncoding(UTF_8);
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            String jsonError = objectMapper.writeValueAsString(new McpError("Session ID missing in message endpoint"));
            PrintWriter writer = response.getWriter();
            writer.write(jsonError);
            writer.flush();
            return;
        }

        // 从 sessions 集合中获取当前 session
        McpServerSession session = sessions.get(sessionId);
        if (session == null) {
            // 如果不包含当前 sessionId 对应的 session,返回异常信息
            response.setContentType(APPLICATION_JSON);
            response.setCharacterEncoding(UTF_8);
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            String jsonError = objectMapper.writeValueAsString(new McpError("Session not found: " + sessionId));
            PrintWriter writer = response.getWriter();
            writer.write(jsonError);
            writer.flush();
            return;
        }

        try {
            // 读取请求体内容,拼接为字符串
            BufferedReader reader = request.getReader();
            StringBuilder body = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                body.append(line);
            }

            // 将拼接后的内容反序列化为 JSON-RPC 消息对象
            McpSchema.JSONRPCMessage message = McpSchema.deserializeJsonRpcMessage(objectMapper, body.toString());

            // 使用 session 执行当前消息,并阻塞直到执行完成
            session.handle(message).block();

            // 设置响应码 200,表示请求成功
            response.setStatus(HttpServletResponse.SC_OK);
        }
        catch (Exception e) {
            logger.error("Error processing message: {}", e.getMessage());
            try {
                McpError mcpError = new McpError(e.getMessage());
                response.setContentType(APPLICATION_JSON);
                response.setCharacterEncoding(UTF_8);
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                String jsonError = objectMapper.writeValueAsString(mcpError);
                PrintWriter writer = response.getWriter();
                writer.write(jsonError);
                writer.flush();
            }
            catch (IOException ex) {
                logger.error(FAILED_TO_SEND_ERROR_RESPONSE, ex.getMessage());
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error processing message");
            }
        }
    }

    /**
     * 优雅地关闭所有已连接客户端的传输并异步释放所有关联资源
     *
     * @return Mono,当关闭成功时完成
     */
    @Override
    public Mono<Void> closeGracefully() {
        // 设置关闭传输标志
        isClosing.set(true);
        logger.debug("Initiating graceful shutdown with {} active sessions", sessions.size());
        // 调用会话层对象关闭传输
        return Flux.fromIterable(sessions.values()).flatMap(McpServerSession::closeGracefully).then();
    }

    /**
     * 向客户端发送 SSE 事件
     *
     * @param writer 发送事件的 writer
     * @param eventType 事件类型(消息或端点)
     * @param data 事件数据
     */
    private void sendEvent(PrintWriter writer, String eventType, String data) throws IOException {
        writer.write("event: " + eventType + "\n");
        writer.write("data: " + data + "\n\n");
        writer.flush();

        if (writer.checkError()) {
            throw new IOException("Client disconnected");
        }
    }

    /**
     * 在 servlet 被销毁时清理资源
     */
    @Override
    public void destroy() {
        closeGracefully().block();
        super.destroy();
    }

}

3.2.4 会话层

MCP 服务端会话层涉及到的类包括 McpSessionMcpServerSessionMcpServerSession.Factory(内部类),如下图所示:

会话层负责与单个客户端建立独立会话、管理请求/通知的收发、并维护连接状态,它将服务端与底层传输通道解耦,使上层逻辑无需关心具体通信细节。其通过 McpSession 接口定义统一操作规范,并在 McpServerSession 提供了具体实现。其核心方法包括:

  • sendRequest():向服务端发送请求消息并等待响应,返回 Mono 表示支持异步处理。
  • sendNotification():向服务端发送无响应的通知消息,返回 Mono 表示支持异步处理。
  • close():立即关闭客户端连接和会话资源。
  • closeGracefully():执行优雅关闭操作,确保未完成的请求正常结束,返回 Mono 表示支持异步处理。

此外,可以看到这里存在工厂接口 McpServerSession.Factory,这个工厂通常由上层(会话管理层)来调用,当一个新的客户端建立连接时,就会调用 create() 方法来生成会话实例。

核心的源码及注释如下所示:

Java 复制代码
/**
 * MCP 会话,用户处理客户端与服务端之间的通信
 * 提供了消息交互和会话生命周期管理的方法
 */
public interface McpSession {

    /**
     * 向服务端发送请求并返回响应结果
     *
     * @param method 调用的方法名
     * @param requestParams 请求参数
     * @param typeRef 反序列化响应结果所需的类型
     * @return Mono,服务端响应后完成
     */
    <T> Mono<T> sendRequest(String method, Object requestParams, TypeReference<T> typeRef);

    /**
     * 向服务端发送不带参数的通知
     *
     * @param method 方法名
     * @return Mono,通知发送后完成
     */
    default Mono<Void> sendNotification(String method) {
        return sendNotification(method, null);
    }

    /**
     * 向服务端发送带参数的通知
     *
     * @param method 通知方法名
     * @param params 方法参数
     * @return Mono,通知发送后完成
     */
    Mono<Void> sendNotification(String method, Object params);

    /**
     * 优雅关闭客户端连接
     *
     * @return Mono,关闭后完成
     */
    Mono<Void> closeGracefully();

    /**
     * 立刻关闭客户端连接
     */
    void close();

}
Java 复制代码
/**
 * MCP 服务端会话实现类,管理与客户端的双向 JSON-RPC 通信
 */
public class McpServerSession implements McpSession {

    private static final Logger logger = LoggerFactory.getLogger(McpServerSession.class);

    // 请求缓存,用于缓存还未处理完的请求
    private final ConcurrentHashMap<Object, MonoSink<McpSchema.JSONRPCResponse>> pendingResponses = new ConcurrentHashMap<>();

    // 会话 id
    private final String id;

    /** Duration to wait for request responses before timing out */
    // 请求超时时间
    private final Duration requestTimeout;

    // 用于生成唯一请求 id 的原子计数器
    private final AtomicLong requestCounter = new AtomicLong(0);

    // 初始化处理器
    private final InitRequestHandler initRequestHandler;

    // 初始化通知处理器
    private final InitNotificationHandler initNotificationHandler;

    // 请求处理器 map
    private final Map<String, RequestHandler<?>> requestHandlers;

    // 通知处理器 map
    private final Map<String, NotificationHandler> notificationHandlers;

    // 传输层对象
    private final McpServerTransport transport;

    private final Sinks.One<McpAsyncServerExchange> exchangeSink = Sinks.one();

    // 客户端能力配置
    private final AtomicReference<McpSchema.ClientCapabilities> clientCapabilities = new AtomicReference<>();

    // 客户端基本信息
    private final AtomicReference<McpSchema.Implementation> clientInfo = new AtomicReference<>();

    private static final int STATE_UNINITIALIZED = 0;

    private static final int STATE_INITIALIZING = 1;

    private static final int STATE_INITIALIZED = 2;

    // 初始化状态
    private final AtomicInteger state = new AtomicInteger(STATE_UNINITIALIZED);

    /**
     * 构造方法
     *
     * @param id 会话 id
     * @param requestTimeout 请求超时时间
     * @param transport 传输层对象
     * @param initHandler 初始化处理器
     * @param initNotificationHandler 初始化通知处理器
     * @param requestHandlers 请求处理器
     * @param notificationHandlers 通知处理器
     */
    public McpServerSession(String id, Duration requestTimeout, McpServerTransport transport,
                            InitRequestHandler initHandler, InitNotificationHandler initNotificationHandler,
                            Map<String, RequestHandler<?>> requestHandlers, Map<String, NotificationHandler> notificationHandlers) {
        this.id = id;
        this.requestTimeout = requestTimeout;
        this.transport = transport;
        this.initRequestHandler = initHandler;
        this.initNotificationHandler = initNotificationHandler;
        this.requestHandlers = requestHandlers;
        this.notificationHandlers = notificationHandlers;
    }

    public String getId() {
        return this.id;
    }

    /**
     * 在客户端和服务端完成初始化后调用
     *
     * @param clientCapabilities 客户端能力配置
     * @param clientInfo 客户端基本信息
     */
    public void init(McpSchema.ClientCapabilities clientCapabilities, McpSchema.Implementation clientInfo) {
        this.clientCapabilities.lazySet(clientCapabilities);
        this.clientInfo.lazySet(clientInfo);
    }

    /**
     * 创建唯一的请求 id
     */
    private String generateRequestId() {
        return this.id + "-" + this.requestCounter.getAndIncrement();
    }

    /**
     * 向客户端发送消息
     *
     * @param method 方法名
     * @param requestParams 方法参数
     * @param typeRef 响应类型
     * @return Mono,发送后完成
     */
    @Override
    public <T> Mono<T> sendRequest(String method, Object requestParams, TypeReference<T> typeRef) {
        // 生成请求 id
        String requestId = this.generateRequestId();

        return Mono.<McpSchema.JSONRPCResponse>create(sink -> {
            // 缓存当前请求
            this.pendingResponses.put(requestId, sink);
            // 构建 JSON-RPC 请求对象
            McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, method,
                    requestId, requestParams);
            // 发送请求
            this.transport.sendMessage(jsonrpcRequest).subscribe(v -> {
            }, error -> {
                this.pendingResponses.remove(requestId);
                sink.error(error);
            });
        }).timeout(requestTimeout).handle((jsonRpcResponse, sink) -> {
            // 处理响应逻辑
            if (jsonRpcResponse.error() != null) {
                // 出现异常
                sink.error(new McpError(jsonRpcResponse.error()));
            }
            else {
                if (typeRef.getType().equals(Void.class)) {
                    // 返回结果类型为空时直接结束
                    sink.complete();
                }
                else {
                    // 返回结果类型不为空时将结果转换为 typeRef 类型对象并返回
                    sink.next(this.transport.unmarshalFrom(jsonRpcResponse.result(), typeRef));
                }
            }
        });
    }

    /**
     * 向服务端发送带参数的通知
     *
     * @param method 通知方法名
     * @param params 方法参数
     * @return Mono,通知发送后完成
     */
    @Override
    public Mono<Void> sendNotification(String method, Object params) {
        // 将方法和参数转换为 JSON-RPC 通知对象
        McpSchema.JSONRPCNotification jsonrpcNotification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION,
                method, params);
        // 使用传输层对象发送消息
        return this.transport.sendMessage(jsonrpcNotification);
    }

    /**
     * 将消息分发到合适的处理程序
     *
     * @param message JSON-RPC 消息
     * @return Mono,消息处理后完成
     */
    public Mono<Void> handle(McpSchema.JSONRPCMessage message) {
        return Mono.defer(() -> {
            if (message instanceof McpSchema.JSONRPCResponse response) {
                // 处理响应类型的消息
                logger.debug("Received Response: {}", response);
                // 获取响应回调并从缓存中移除当前请求,表示当前请求处理完毕
                var sink = pendingResponses.remove(response.id());
                if (sink == null) {
                    logger.warn("Unexpected response for unknown id {}", response.id());
                }
                else {
                    // 将响应传递给订阅者
                    sink.success(response);
                }
                return Mono.empty();
            }
            else if (message instanceof McpSchema.JSONRPCRequest request) {
                // 处理请求类型的消息
                logger.debug("Received request: {}", request);
                return handleIncomingRequest(request).onErrorResume(error -> {
                    // 处理失败时返回异常类型响应
                    var errorResponse = new McpSchema.JSONRPCResponse(McpSchema.JSONRPC_VERSION, request.id(), null,
                            new McpSchema.JSONRPCResponse.JSONRPCError(McpSchema.ErrorCodes.INTERNAL_ERROR,
                                    error.getMessage(), null));
                    return this.transport.sendMessage(errorResponse).then(Mono.empty());
                }).flatMap(this.transport::sendMessage);
            }
            else if (message instanceof McpSchema.JSONRPCNotification notification) {
                // 处理通知类型的消息
                logger.debug("Received notification: {}", notification);
                return handleIncomingNotification(notification)
                        .doOnError(error -> logger.error("Error handling notification: {}", error.getMessage()));
            }
            else {
                logger.warn("Received unknown message type: {}", message);
                return Mono.empty();
            }
        });
    }

    /**
     * 将 JSON-RPC 通知类型消息路由到相应的处理程序来处理
     *
     * @param notification JSON-RPC 通知
     * @return Mono,通知处理后完成
     */
    private Mono<Void> handleIncomingNotification(McpSchema.JSONRPCNotification notification) {
        return Mono.defer(() -> {
            if (McpSchema.METHOD_NOTIFICATION_INITIALIZED.equals(notification.method())) {
                // 初始化完成通知方法执行逻辑
                // 状态设置为初始化完成
                this.state.lazySet(STATE_INITIALIZED);
                exchangeSink.tryEmitValue(new McpAsyncServerExchange(this, clientCapabilities.get(), clientInfo.get()));
                return this.initNotificationHandler.handle();
            }

            // 根据方法名获取处理器
            var handler = notificationHandlers.get(notification.method());
            if (handler == null) {
                // 处理器不存在时返回空信息
                logger.error("No handler registered for notification method: {}", notification.method());
                return Mono.empty();
            }
            return this.exchangeSink.asMono().flatMap(exchange -> handler.handle(exchange, notification.params()));
        });
    }

    /**
     * 表示方法未找到的错误数据结构
     *
     * @param method 方法名
     * @param message 异常信息
     * @param data 附加数据
     */
    record MethodNotFoundError(String method, String message, Object data) {
    }

    /**
     * 根据方法名返回对应的 MethodNotFoundError 对象
     *
     * @param method 方法名
     * @return MethodNotFoundError 对象
     */
    private MethodNotFoundError getMethodNotFoundError(String method) {
        return new MethodNotFoundError(method, "Method not found: " + method, null);
    }

    /**
     * 优雅关闭客户端连接
     *
     * @return Mono,关闭后完成
     */
    @Override
    public Mono<Void> closeGracefully() {
        return this.transport.closeGracefully();
    }

    /**
     * 立刻关闭客户端连接
     */
    @Override
    public void close() {
        this.transport.close();
    }

}

3.2.5 传输层

MCP 服务端传输层涉及到的类包括:McpTransportMcpServerTransportHttpServletSseServerTransportProvider.HttpServletMcpSessionTransport (内部类)、StdioServerTransportProvider.StdioMcpSessionTransport (内部类) 等。其中 HttpServletMcpSessionTransportStdioMcpSessionTransportMcpServerTransport 的实现类,分别对应 SSE 和 Stdio 的具体实现,这里仅列出两种,实际上不同的传输协议都会有对应的实现类。其类图如下所示:

核心接口是 McpTransport 和其子接口 McpServerTransport,其中 McpServerTransport 内部为空,即服务端的传输层类还没有扩展其他的功能。McpTransport 中核心方法的功能已在 MCP 客户端传输层给出,这里不再赘述。

核心的源码及注释如下所示(McpTransport 在 MCP 客户端传输层已给出,这里也不再赘述。实现类中仅给出 HttpServletMcpSessionTransport 的具体实现,其他的实现类是类似的):

Java 复制代码
/**
 * HttpServlet SSE 会话的 McpServerTransport 实现类,处理特定客户端会话的传输层通信
 */
private class HttpServletMcpSessionTransport implements McpServerTransport {

    // 会话 id
    private final String sessionId;

    // 异步上下文
    private final AsyncContext asyncContext;

    // 写入器,用于向客户端发送消息
    private final PrintWriter writer;

    /**
     * 构造方法
     *
     * @param sessionId 会话 id
     * @param asyncContext 异步会话上下文
     * @param writer 用于向客户端发送服务端消息的写入器
     */
    HttpServletMcpSessionTransport(String sessionId, AsyncContext asyncContext, PrintWriter writer) {
        this.sessionId = sessionId;
        this.asyncContext = asyncContext;
        this.writer = writer;
        logger.debug("Session transport {} initialized with SSE writer", sessionId);
    }

    /**
     * 通过 SSE 连接向客户端发送 JSON-RPC 消息
     *
     * @param message JSON-RPC 消息
     * @return Mono,在消息发送完成后返回结果
     */
    @Override
    public Mono<Void> sendMessage(McpSchema.JSONRPCMessage message) {
        return Mono.fromRunnable(() -> {
            try {
                // 将消息序列化为字符串
                String jsonText = objectMapper.writeValueAsString(message);
                // 发送消息
                sendEvent(writer, MESSAGE_EVENT_TYPE, jsonText);
                logger.debug("Message sent to session {}", sessionId);
            }
            catch (Exception e) {
                logger.error("Failed to send message to session {}: {}", sessionId, e.getMessage());
                sessions.remove(sessionId);
                asyncContext.complete();
            }
        });
    }

    /**
     * 通过已配置的 ObjectMapper 将数据从一种类型转换为另一种类型
     *
     * @param data 源数据
     * @param typeRef 目标类型
     * @return 转换后的对象
     */
    @Override
    public <T> T unmarshalFrom(Object data, TypeReference<T> typeRef) {
        return objectMapper.convertValue(data, typeRef);
    }

    /**
     * 优雅地关闭当前传输
     *
     * @return Mono,关闭传输后返回结果
     */
    @Override
    public Mono<Void> closeGracefully() {
        return Mono.fromRunnable(() -> {
            logger.debug("Closing session transport: {}", sessionId);
            try {
                // 从已注册的 sessions 集合中移除当前 session
                sessions.remove(sessionId);
                // 结束异步长下文
                asyncContext.complete();
                logger.debug("Successfully completed async context for session {}", sessionId);
            }
            catch (Exception e) {
                logger.warn("Failed to complete async context for session {}: {}", sessionId, e.getMessage());
            }
        });
    }

    /**
     * 立刻关闭当前传输
     */
    @Override
    public void close() {
        try {
            // 从已注册的 sessions 集合中移除当前 session
            sessions.remove(sessionId);
            // 结束异步长下文
            asyncContext.complete();
            logger.debug("Successfully completed async context for session {}", sessionId);
        }
        catch (Exception e) {
            logger.warn("Failed to complete async context for session {}: {}", sessionId, e.getMessage());
        }
    }

}
相关推荐
骄马之死5 小时前
SpringMVC + SpringBoot 核心知识点总结
java·spring boot·后端
GoGeekBaird6 小时前
Anthropic技能"(Skills)的经验分享
后端
王码码20357 小时前
多台服务器怎么统一看状态?Beszel 轻量监控,搭起来不费事
运维·服务器·后端·安全·阿里云·接口·web
刀法如飞7 小时前
一文搞懂DDD 领域驱动设计思想原理
设计模式·架构·代码规范
郑洁文7 小时前
基于Spring Boot的流浪动物救助网站
java·spring boot·后端·毕设·流浪动物救助
螺丝钉code8 小时前
JAVA项目 Claude code CLAUDE.md 到底应该怎么写
java·人工智能·claude code
Cosolar8 小时前
LlamaIndex 文档解析与分块策略深度解析
人工智能·面试·架构
指令集梦境8 小时前
Cursor + Spring Boot实战:从零写一个RESTful API
spring boot·后端·restful
摇滚侠9 小时前
Maven 入门+高深 单一架构案例 54-59
java·架构·maven·intellij-idea
caimouse9 小时前
Reactos 第 4 章 对象管理 — 4.5 几个常用的内核函数
c语言·windows·架构