# Strait
**Repository Path**: mx-team/strait
## Basic Information
- **Project Name**: Strait
- **Description**: 让 Java 8 / Spring Boot 2.x 存量系统以低侵入方式暴露标准 MCP Server 能力
- **Primary Language**: Java
- **License**: Apache-2.0
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2026-06-17
- **Last Updated**: 2026-07-26
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Strait MCP Java Framework
Java 8 优先的 MCP(Model Context Protocol)框架,为 Spring Boot 2.x 存量系统提供低侵入 MCP Server 能力。
**一句话:** 在你的 Java 8 / Spring Boot 2 老项目里加个 starter,写几个 Provider 或注解,就能暴露标准 MCP 服务。
---
## Quickstart
### 1. 添加依赖
```xml
com.strait
strait-mcp-spring-boot2-starter
0.1.0-SNAPSHOT
```
### 2. 启动 MCP Server
```java
@SpringBootApplication
@EnableStraitMcpServer
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
```
启动后,服务端暴露 `http://localhost:8080/mcp` 端点。
### 3. 用注解声明一个工具
```java
@Component
@McpTools(name = "hello", description = "问候工具")
public class HelloTools {
@McpTool(name = "say", description = "返回问候消息")
public String say(@McpParam("名称") String name) {
return "Hello, " + name + "!";
}
}
```
### 4. 用 curl 测试完整流程
```bash
# ① initialize
INIT_RESP=$(curl -s -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: 2025-11-25" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"test-client","version":"1.0"}}}')
# 从响应头中提取 SessionId
SESSION_ID=$(echo "$INIT_RESP" | grep -i 'MCP-Session-Id' | awk '{print $2}' | tr -d '\r')
# ② notifications/initialized(标记就绪)
curl -s -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: 2025-11-25" -H "MCP-Session-Id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","method":"notifications/initialized"}'
# ③ tools/list
curl -s -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: 2025-11-25" -H "MCP-Session-Id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
# ④ tools/call
curl -s -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: 2025-11-25" -H "MCP-Session-Id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"hello.say","arguments":{"name":"World"}}}'
# ⑤ 删除会话
curl -i -X DELETE http://localhost:8080/mcp \
-H "Accept: application/json, text/event-stream" -H "MCP-Protocol-Version: 2025-11-25" \
-H "MCP-Session-Id: $SESSION_ID"
```
---
## 项目模块
| 模块 | 说明 | 适用场景 |
|------|------|---------|
| `strait-mcp-api` | MCP 协议模型、SPI 接口、注解定义(纯 JDK,无外部依赖) | 定义 Tool / Prompt / Resource 时需要引入 |
| `strait-mcp-core` | 协议内核:JSON-RPC 调度、Session 管理、事件存储 | 框架内部使用,无需直接依赖 |
| `strait-mcp-codec-jackson` | Jackson 2.18.x 的 JSON 编解码实现 | 框架内部使用,无需直接依赖 |
| `strait-mcp-server` | 服务端运行时:Handler 注册、请求分发、生命周期管理 | 框架内部使用,无需直接依赖 |
| `strait-mcp-transport-servlet-javax` | Servlet 3.1 (javax) 传输层:JSON / SSE / Streamable HTTP | 提供 Servlet 端点,由 starter 自动装配 |
| **`strait-mcp-spring-boot2-starter`** | **Spring Boot 2.x 自动装配(用户主入口)** | **必选**:引入即用,自动注册端点 + 扫描注解 |
| `strait-mcp-client` | MCP 客户端 SDK:直连模式、负载均衡、熔断器 | 需要调用其他 MCP Server 时使用 |
| `strait-mcp-router` | MCP Router:服务目录 + 路由代理,聚合多个后端 MCP Server | 多服务聚合、统一入口场景 |
| `strait-mcp-testkit` | 协议兼容性测试工具 | 验证 Server 端协议实现是否正确 |
| `examples/spring-boot2-java8-server` | 可运行示例工程(含完整 Tool / Prompt / Resource 示例) | 快速上手参考 |
---
## 里程碑状态
| 里程碑 | 目标 | 状态 |
|--------|------|------|
| **M0 — 协议基础** | JSON-RPC 核心、协议版本协商、初始化握手 | ✅ 已完成 |
| **M0.1 — Session 管理** | Session 创建/销毁/超时、SessionStore SPI | ✅ 已完成 |
| **M0.2 — 基础错误处理** | McpErrorCode 定义、结构化错误响应 | ✅ 已完成 |
| **M1 — 核心能力** | | |
| **M1.0 — Tool** | Provider SPI + 注解式 @McpTool / @McpTools / @McpParam / @McpModel | ✅ 已完成 |
| **M1.1 — Prompt** | Provider SPI + Resolver + 注解式 @McpPrompt / @McpPrompts | ✅ 已完成 |
| **M1.2 — Resource** | Provider SPI + Resolver + 注解式 @McpResource / @McpResources | ✅ 已完成 |
| **M2 — 传输层** | | |
| **M2.0 — Servlet 传输** | Servlet 3.1 (javax) 端点,JSON response 模式 | ✅ 已完成 |
| **M2.1 — Streamable HTTP** | GET SSE / POST SSE / RESUMABLE_SSE 三种模式 | ✅ 已完成 |
| **M2.2 — Spring Boot Starter** | 自动装配、注解扫描、属性配置 | ✅ 已完成 |
| **M3 — 生态** | | |
| **M3.0 — Client SDK** | 直连模式、负载均衡(Random / RoundRobin / Weighted / FirstAvailable)、熔断器(SimpleCircuitBreaker)、服务目录 | ✅ 已完成 |
| **M3.1 — Router** | 静态服务目录 + 路由代理、数据库服务目录(JdbcMcpServiceDirectory) | ✅ 已完成 |
| **M3.2 — Spring Boot 3 适配** | Spring Boot 3.x / Jakarta Servlet 支持 | 📋 规划中 |
| **M3.3 — 企业特性** | 鉴权 / 限流 / 审计 / 可观测性 | 📋 规划中 |
| **M3.4 — 协议扩展** | JSON-RPC Batch、通知消息、Ping 保活 | 📋 规划中 |
---
## 定义工具(Tool)
**方式一:实现 SPI**
```java
@Component
public class HelloTool implements ToolProvider {
@Override public String category() { return "tool"; }
@Override
public List list(McpContext context) {
ToolDefinition def = new ToolDefinition();
def.setName("hello");
def.setDescription("返回问候消息");
return Collections.singletonList(def);
}
@Override
public CallToolResult call(CallToolRequest request) {
String name = request.getArguments().get("name").toString();
ContentBlock block = new ContentBlock();
block.setText("Hello, " + name + "!");
CallToolResult result = new CallToolResult();
result.setContent(Collections.singletonList(block));
return result;
}
}
```
**方式二:注解声明(更轻量)**
```java
@Component
@McpTools(name = "hello", description = "问候工具")
public class HelloTools {
@McpTool(name = "say", description = "返回问候消息")
public String say(@McpParam("名称") String name) {
return "Hello, " + name + "!";
}
}
```
---
## 定义提示词(Prompt)
**方式一:实现 SPI**
```java
@Component
public class MyPromptProvider implements PromptProvider {
@Override public String category() { return "prompt"; }
@Override
public List list(McpContext context) {
PromptDefinition def = new PromptDefinition();
def.setName("review");
def.setDescription("代码审查提示");
PromptArgument arg = new PromptArgument();
arg.setName("code");
arg.setDescription("待审查代码");
arg.setRequired(true);
def.setArguments(Collections.singletonList(arg));
return Collections.singletonList(def);
}
}
@Component
public class MyPromptResolver implements PromptResolver {
@Override
public GetPromptResult get(GetPromptRequest request, McpContext context) {
String code = request.getArguments().get("code").toString();
TextContent content = new TextContent();
content.setText("请审查这段代码:\n" + code);
PromptMessage msg = new PromptMessage();
msg.setRole("user");
msg.setContent(content);
GetPromptResult result = new GetPromptResult();
result.setMessages(Collections.singletonList(msg));
return result;
}
}
```
**方式二:注解声明**
```java
@Component
@McpPrompts(name = "review", description = "代码审查")
public class ReviewPrompts {
@McpPrompt(name = "code", description = "审查代码")
public GetPromptResult reviewCode(@McpParam("待审查代码") String code) {
TextContent content = new TextContent();
content.setText("请审查这段代码:\n" + code);
PromptMessage msg = new PromptMessage();
msg.setRole("user");
msg.setContent(content);
GetPromptResult result = new GetPromptResult();
result.setMessages(Collections.singletonList(msg));
return result;
}
}
```
---
## 定义资源(Resource)
**方式一:实现 SPI**
```java
@Component
public class MyResourceProvider implements ResourceProvider { ... }
@Component
public class MyResourceResolver implements ResourceResolver { ... }
```
**方式二:注解声明**
```java
@Component
@McpResources(name = "docs", description = "文档资源")
public class DocResources {
@McpResource(name = "readme", uri = "strait://docs/readme",
description = "README 文档", mimeType = "text/plain")
public ReadResourceResult getReadme() {
ResourceTextContent content = new ResourceTextContent();
content.setUri("strait://docs/readme");
content.setText("# Strait MCP\n\n示例资源内容。");
ReadResourceResult result = new ReadResourceResult();
result.setContents(Collections.singletonList(content));
return result;
}
}
```
---
## 启用 SSE 流式响应
默认使用 JSON response 模式。如需 SSE streaming 和 resumability,需显式注册带配置的 Servlet:
```java
@Bean
public ServletRegistrationBean mcpSseServlet(
McpServer mcpServer, McpJsonCodec codec) {
StreamableHttpServletConfig config = new StreamableHttpServletConfig();
config.setMode(StreamableHttpMode.BASIC_SSE); // 或 POST_SSE / RESUMABLE_SSE
McpStreamRegistry streamRegistry = new InMemoryMcpStreamRegistry(
new DefaultMcpStreamIdGenerator());
McpEventStore eventStore = new InMemoryMcpEventStore(1000, 1024 * 1024);
McpOutboundChannel outboundChannel = new DefaultMcpOutboundChannel(
streamRegistry, eventStore);
McpServlet servlet = new McpServlet(mcpServer, codec, null,
config, streamRegistry, outboundChannel, eventStore);
return new ServletRegistrationBean<>(servlet, "/mcp");
}
```
支持的四种模式:
| 模式 | GET /mcp | POST JSON | POST SSE | Last-Event-ID |
|------|----------|-----------|----------|---------------|
| `JSON_ONLY` | 405 | ✅ | — | — |
| `BASIC_SSE` | SSE stream | ✅ | — | ❌ 返回 400 |
| `POST_SSE` | SSE stream | ✅ | ✅(可配置方法) | ❌ 返回 400 |
| `RESUMABLE_SSE` | SSE + replay | ✅ | ✅ | ✅ |
---
## 使用 Client SDK
`strait-mcp-client` 模块提供了直连 MCP Server 的客户端:
```java
McpClient client = McpClient.builder()
.jsonCodec(new JacksonMcpJsonCodec(objectMapper))
.transport(new HttpUrlConnectionMcpClientTransport())
.defaultEndpoint("my-service", "http://localhost:8080/mcp")
.build();
// 握手
InitializeResult init = client.initialize(McpCallOptions.defaultServer());
client.initialized(McpCallOptions.defaultServer());
// 发现工具
ListToolsResult tools = client.listTools(McpCallOptions.defaultServer());
// 调用工具
Map args = new LinkedHashMap<>();
args.put("message", "Hello");
CallToolResult result = client.callTool("hello.say", args,
McpCallOptions.defaultServer());
// 发现提示词
ListPromptsResult prompts = client.listPrompts(McpCallOptions.defaultServer());
// 获取提示词
GetPromptResult prompt = client.getPrompt("review.code",
Collections.singletonMap("code", "print('hello')"),
McpCallOptions.defaultServer());
// 发现资源
ListResourcesResult resources = client.listResources(McpCallOptions.defaultServer());
// 读取资源
ReadResourceResult resource = client.readResource("strait://docs/readme",
McpCallOptions.defaultServer());
// 删除会话
client.deleteSession(McpCallOptions.defaultServer());
```
---
## 部署 Router
`strait-mcp-router` 模块提供 MCP Router,支持聚合多个后端 MCP Server:
```yaml
strait:
mcp:
router:
services:
- serverCode: billing
endpoint: http://billing-app/mcp
toolNamespace: billing
- serverCode: inventory
endpoint: http://inventory-app/mcp
toolNamespace: inventory
```
Router 对外暴露标准 MCP `/mcp` 端点,Client 只访问 Router 即可:
```
Client → Router → billing (工具: billing.calc/add)
→ inventory (工具: inventory.stock/query)
```
---
## 当前能力边界
| 能力 | 状态 |
|------|------|
| Tool(Provider SPI + 注解式) | ✅ 已实现 |
| Prompt(Provider SPI + Resolver + 注解式) | ✅ 已实现 |
| Resource(Provider SPI + Resolver + 注解式) | ✅ 已实现 |
| Streamable HTTP JSON response | ✅ 已实现(默认) |
| Streamable HTTP GET SSE | ✅ 已实现(需显式配置) |
| Streamable HTTP POST SSE response | ✅ 已实现(需显式配置) |
| `Last-Event-ID` resumability | ✅ 已实现(需显式配置) |
| Client SDK(直连模式) | ✅ 已实现 |
| Router(静态服务目录 + 路由代理) | ✅ 已实现 |
| 数据库服务目录(JdbcMcpServiceDirectory) | ✅ 已实现 |
| 客户端熔断器(SimpleCircuitBreaker + 时间窗口自动恢复) | ✅ 已实现 |
| 客户端负载均衡(Random / RoundRobin / Weighted / FirstAvailable) | ✅ 已实现 |
| JSON-RPC batch | ❌ 不支持 |
| 鉴权 / 限流 / 审计 | 📋 Roadmap |
---
## 技术栈
- Java 8 编译/运行
- Spring Boot 2.3+ / 2.7.x
- Jackson 2.18.x LTS
- SLF4J 1.7.x
- Servlet 3.1 (javax)
- JUnit 4 + AssertJ + Mockito
## 许可证
Apache License 2.0