AI MCP Gateway 学习入门

Note

目标:先看懂 AI MCP Gateway 前 3 章在做什么,不追求一口气吃透全部 MCP 规范。

这份项目本质上在做什么

可以先把它理解成:

  • 对外,它是一个 MCP Server
  • 对内,它想继续发展成一个 MCP Gateway

当前前几章重点其实只有两件事:

  • 让 Java 服务能听懂 MCP 的基础消息格式
  • 让服务能管理会话,并把请求分发到不同处理逻辑

2 小时最小阅读清单

1. JSON-RPC 2.0

用途:先看懂 initializetools/listtools/call 这些消息为什么长这样。

你只要先搞明白:

  • 请求长什么样
  • 响应长什么样
  • 错误怎么返回
  • 为什么客户端和服务端要靠 id 对上

2. MCP 基础总览

用途:知道 MCP 是什么,不要把它误解成普通 REST 接口。

重点看:

  • initialize 为什么是握手
  • client / server 各自负责什么
  • MCP 里为什么会有 tools、resources、prompts

Client and server capabilities establish which optional protocol features will be available during the session.Key capabilities include:

CategoryCapabilityDescription
ClientrootsAbility to provide filesystem roots
ClientsamplingSupport for LLM sampling requests
ClientelicitationSupport for server elicitation requests
ClienttasksSupport for task-augmented client requests
ClientexperimentalDescribes support for non-standard experimental features
ServerpromptsOffers prompt templates
ServerresourcesProvides readable resources
ServertoolsExposes callable tools
ServerloggingEmits structured log messages
ServercompletionsSupports argument autocompletion
ServertasksSupport for task-augmented server requests
ServerexperimentalDescribes support for non-standard experimental features

Capability objects can describe sub-capabilities like:

  • listChanged: Support for list change notifications (for prompts, resources, and tools)
  • subscribe: Support for subscribing to individual items’ changes (resources only)

3. MCP 传输层

用途:理解你这个项目为什么用了 SSE,以及为什么现在主流转成 Streamable HTTP

重点看:

  • stdioStreamable HTTP 的区别

  • 新规范为什么替换旧的 HTTP + SSE 方案

In the Streamable HTTP transport, the server operates as an independent process that can handle multiple client connections. This transport uses HTTP POST and GET requests. Server can optionally make use of Server-Sent Events (SSE) to stream multiple server messages. This permits basic MCP servers, as well as more feature-rich servers supporting streaming and server-to-client notifications and requests.The server MUST provide a single HTTP endpoint path (hereafter referred to as the MCP endpoint) that supports both POST and GET methods. For example, this could be a URL like https://example.com/mcp.

  • 为什么你手上的项目代码会显得“有点旧,但还能学”

4. MCP 的 Tools

用途:这是这个项目后续最核心的能力。

重点看:

  • tools/list 是告诉客户端“我有哪些能力”
  • tools/call 是真正执行某个能力
  • tool 的输入输出为什么要结构化

5. Spring WebFlux

用途:看懂项目里的 MonoFluxServerSentEvent

重点看:

  • Mono 表示 0 或 1 个结果
  • Flux 表示多个结果或流
  • 为什么 SSE 响应通常会返回 Flux

6. Reactor 的 Flux / Mono / Sinks

用途:看懂这个项目的 session 管理为什么能持续推消息。

重点看:

  • FluxMono 的基本含义
  • Sinks.Many 是什么
  • 为什么 session 可以往 sink 里不断发消息

7. Spring AI 的 MCP 文档

用途:等你看完当前项目前几章后,再对照 Java 生态里的更新实现。

重点看:

  • Java 里现在一般怎么做 MCP
  • 哪些东西项目里手写了,哪些东西新版框架已经帮你封装了

你现在的最小学习顺序

  1. 先看 JSON-RPC
  2. 再看 MCP Overview + Lifecycle + Tools
  3. 再看 WebFlux + Reactor Sinks
  4. 再回来看项目里的 Controller -> SessionManagementService -> Handler
  5. 最后再看 Spring AI MCP,建立“旧项目实现 vs 新生态做法”的对照

看当前项目时要重点盯住的问题

看到 3-1 ~ 3-3 时

只问自己这 4 个问题:

  • 请求先从哪个 Controller 进来
  • 为什么先创建 sessionId
  • SSE 长连接在这里负责什么
  • Case / Domain / Trigger 是怎么分工的

看到 3-4 ~ 3-10 时

继续问:

  • initialize 为什么是第一步
  • tools/list 为什么像“能力目录”
  • tools/call 为什么像“实际调用”
  • handler 分发为什么适合做协议处理

看到 3-11 以后

再关注:

  • 会话编排和鉴权为什么更像“网关职责”
  • OpenAPI 解析为什么说明它要把已有 HTTP 接口转成 MCP 工具

当前阶段不要纠结的点

Warning

先不要因为“现在主流是 Streamable HTTP,不是旧 SSE”就否定这套内容。

对你现在来说,先学清楚 MCP 消息模型 + 会话 + tools 才是收益最大的。传输层更新是第二步。

我对自己的检查标准

学完当前阶段后,至少应该能自己讲清楚:

  • 这个项目为什么说自己是 MCP Gateway
  • 它和普通 REST 项目最大的区别是什么
  • initializetools/listtools/call 各自做什么
  • 为什么这里要有会话和 SSE
  • 哪些是旧实现,哪些是 MCP 核心思想

后续可补的书

  • 《Spring 实战》:补 Spring Boot 基础
  • 《Reactive Spring》:补 WebFlux / Reactor
  • 《实现领域驱动设计》:补这个项目的分层理解

备注

如果后面继续学这个项目,可以再补一篇对照笔记:

  • 当前项目怎么做
  • 最新 MCP Streamable HTTP 应该怎么做
  • Spring AI MCP 现在怎么做

2.SSE(server sent-events)

统一端点:移除了专门建立连接的 /sse 端点,将所有通信整合到统一的端点。

按需流式传输:服务器可以灵活选择返回标准 HTTP 响应或通过 SSE 流式返回。

状态管理:引入 session 机制以支持状态管理和恢复。

3. Reactor 的 Flux / Mono / Sinks

Note

这一部分是为了看懂项目里的响应式写法,不是为了把响应式编程一次学完。你当前只需要先搞清楚三个名词分别代表什么。

1. 先记住三个最重要的定义

  • Mono<T>:最多产生 1 个结果。可以理解成“异步版的一个值”。
  • Flux<T>:可以产生 0 个到多个结果。可以理解成“异步版的一个列表,或者持续不断的数据流”。
  • Sinks.Many<T>:一个可以主动往外推数据的“发送器”。它经常和 Flux 搭配使用。

如果用传统 Java 的方式类比:

  • Mono<User> 有点像“将来会返回一个 User
  • Flux<String> 有点像“将来会陆续返回很多个 String
  • Sinks.Many<String> 有点像“我手里有个消息出口,可以不断往外发字符串”

2. 为什么 WebFlux 里不是直接返回对象,而是 Mono / Flux

在传统 Spring MVC 里,你常见的是:

public User queryUser()

或者:

public List<User> queryUsers()

但在 WebFlux 里,返回值通常变成:

public Mono<User> queryUser()
public Flux<User> queryUsers()

原因不是“故意写复杂”,而是因为它想表达:

  • 这个结果可能稍后才到
  • 这个结果可能是持续不断来的
  • 整个处理链条都按异步和流式方式运行

3. Mono 到底适合什么场景

Mono 适合“一个请求只对应一个结果”的情况。

比如:

  • 查询单个用户
  • 返回一次 HTTP 响应
  • 创建订单后返回一个结果对象

你这个项目里有这样的写法:

Mono<ResponseEntity<Object>>

它的意思就是:

  • 这个接口最终会返回一个 HTTP 响应
  • 但这个响应是按响应式方式包装起来的

4. Flux 到底适合什么场景

Flux 适合“多个结果”或者“连续不断的事件流”。

比如:

  • 查询很多条数据
  • 聊天消息流
  • SSE 推送
  • 日志流
  • 心跳消息

你这个项目里最关键的一段就是:

Flux<ServerSentEvent<String>>

这表示:

  • 返回的不是一个普通对象
  • 而是一串不断往客户端发的 SSE 事件

这也是为什么它适合拿来做会话连接。

5. Sinks.Many 是干什么的

这是你当前最值得搞懂的点。

Sinks.Many<T> 可以理解成:

  • 我先准备一个“消息出口”
  • 后面任何时候,只要有新消息,我都可以 emit 进去
  • 订阅这个出口的人,就能持续收到消息

一个非常粗糙但好理解的类比:

  • Flux 像一条河流
  • Sinks.Many 像你手里的水龙头
  • 你一开水龙头,水就流进河道

在这个项目里,session 之所以能持续往客户端推消息,就是因为每个会话都持有一个 sink

6. 这个项目里它们是怎么配合的

你项目中的思路大致是:

  1. 创建会话时,先创建一个 Sinks.Many<ServerSentEvent<String>>
  2. 把这个 sink 保存到当前 session 配置里
  3. 对外返回 sink.asFlux()
  4. 后面只要有消息,就往 sinkemit
  5. 客户端那边因为连着 SSE,就会持续收到消息

所以这里的核心关系是:

  • Sinks.Many 负责“发”
  • Flux 负责“流”
  • SSE 负责“把这个流传给客户端”

7. 对照你这个项目的代码理解

你可以重点看这两个位置:

  • SessionManagementService#createSession
  • EndNode#doApply

大意是:

  • createSession 里创建 sink,并先发一个 endpoint 消息
  • EndNode 里把 sink.asFlux() 返回出去
  • 同时还 mergeWith 了一个心跳 Flux.interval(...)

这就意味着客户端连接上以后,会持续收到两类东西:

  • 业务消息
  • 心跳消息

8. 为什么这里还要有心跳

因为 SSE 是长连接。

如果连接太久没有任何数据,有些中间层或者客户端可能会认为连接空闲,从而断开。 所以会定期发:

  • ping
  • keep-alive

这也是 Flux 很适合做这类场景的原因,因为它天然适合描述“每隔一段时间发一个事件”。

9. 你现在不用深究的内容

先不用急着学这些:

  • 背压
  • 调度器切换
  • 冷流 / 热流 的完整理论
  • 各种复杂操作符

你现在只要先记住:

  • Mono:一个结果
  • Flux:多个结果 / 持续流
  • Sinks.Many:主动发消息的入口

10. 用一句话串起来

在这个项目里:

  • Mono 用来表达“最终返回一个响应”
  • Flux 用来表达“持续返回一串事件”
  • Sinks.Many 用来表达“服务端主动往这串事件里塞数据”

11. 你看项目时可以这样自查

如果你看到下面这些代码,能说出它们是什么意思,就说明这一节已经够用了:

  • Mono<ResponseEntity<Object>>
  • Flux<ServerSentEvent<String>>
  • Sinks.many().multicast().onBackpressureBuffer()
  • sink.asFlux()
  • Flux.interval(...)

12. 一个最小示意

Sinks.Many<String> sink = Sinks.many().multicast().onBackpressureBuffer();
 
Flux<String> flux = sink.asFlux();
 
sink.tryEmitNext("hello");
sink.tryEmitNext("world");

你可以把它理解成:

  • 先创建一个可推送消息的出口
  • 再把它转换成一个流
  • 每次 tryEmitNext(...),订阅这个流的人就能收到新消息

Tip

如果只为看懂当前项目,先把 Mono / Flux / Sinks 当成“响应式容器 + 消息出口”就够了。 真正的响应式编程细节,等项目走通以后再补。