# 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