图解源码:万字解析 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());
        }
    }

}
相关推荐
好学且牛逼的马2 小时前
【SSM 框架 | day27 spring MVC】
java
是烟花哈2 小时前
后端开发CRUD实现
java·开发语言·spring boot·mybatis
JELEE.2 小时前
Django中的clean()方法和full_clean()方法
后端·python·django
aiopencode2 小时前
iOS 上架工具全解析,从 Xcode 到 开心上架(Appuploader)跨平台命令行免 Mac 上传指南
后端
爱分享的鱼鱼2 小时前
Java基础(六:线程、线程同步,线程池)
java·后端
申阳3 小时前
Day 8:06. 基于Nuxt开发博客项目-我的服务模块开发
前端·后端·程序员
quant_19863 小时前
全面解析美股行情API
经验分享·后端·python·websocket·程序人生·区块链
随便叫个啥呢3 小时前
java使用poi-tl模版+vform自定义表单生成word,使用LibreOffice导出为pdf,批量下载为压缩文件
java·pdf·word·zip
CodeCraft Studio3 小时前
国产化Word处理控件Spire.Doc教程:使用Java将RTF文件转换为PDF的全面教程
java·pdf·word·spire.doc·rtf转pdf·文件格式转换·文档开发sdk
随便叫个啥呢3 小时前
java使用poi-tl模版+vform自定义表单生成word
java·word·poi-tl