Skip to content

Commit 2c44e7e

Browse files
Aias00claude
andauthored
feat(mcp-server): enhance SDK compatibility with reflection caching and error handling (apache#6312)
* feat(mcp-server): enhance SDK compatibility with reflection caching and error handling - Add reflection field caching in McpSessionHelper for better performance and reliability - Add SUPPORTED_SDK_VERSION constant for SDK compatibility tracking - Enhance error messages with SDK version information in ShenyuToolCallback - Add SDK compatibility notes in pom.xml documenting reflection usage - Update MCP_TOOL_EXAMPLES.md and MCP_TOOL_EXAMPLES_EN.md with SDK version compatibility table This change improves compatibility with MCP SDK 0.17.0 by: - Caching reflection fields at class load time - Adding graceful degradation when reflection fails - Documenting known limitations and supported SDK versions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix mcp sdk compatibility error handling --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d45cd70 commit 2c44e7e

6 files changed

Lines changed: 312 additions & 70 deletions

File tree

shenyu-plugin/shenyu-plugin-mcp-server/MCP_TOOL_EXAMPLES.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@
22

33
本文档基于 **shenyu-examples-http** 项目中的真实接口,提供了 Shenyu MCP Server Plugin 的各种工具配置示例,涵盖不同的 HTTP 请求方法、参数类型和配置方式。
44

5+
## SDK 版本兼容性
6+
7+
本插件基于以下 SDK 版本开发和测试:
8+
9+
| 依赖 | 版本 |
10+
|------|------|
11+
| MCP SDK (io.modelcontextprotocol.sdk:mcp-bom) | 0.17.0 |
12+
| Spring AI (org.springframework.ai:spring-ai-bom) | 1.1.2 |
13+
| Spring Boot | 3.3.1 |
14+
15+
### 支持的协议
16+
17+
- **SSE (Server-Sent Events)**: `/sse` 端点,支持长连接会话
18+
- **Streamable HTTP**: 统一端点,支持 GET (SSE 流) 和 POST (消息) 请求
19+
20+
### 已知限制
21+
22+
1. **Session 管理**: 使用反射机制访问 SDK 内部字段获取 Session ID,SDK 版本升级可能影响兼容性
23+
2. **工具调用超时**: 默认 60 秒,可通过 `requestTemplate.timeout` 配置
24+
3. **CORS 支持**: 通过 `shenyu.cross.allowedHeaders` 配置允许的请求头
25+
526
## 1. 简单 GET 请求示例
627

728
### 1.1 无参数 GET 请求

shenyu-plugin/shenyu-plugin-mcp-server/MCP_TOOL_EXAMPLES_EN.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@
22

33
This document provides comprehensive examples of tool configurations for the Shenyu MCP Server Plugin based on **real interfaces from the shenyu-examples-http project**, covering different HTTP request methods, parameter types, and configuration patterns.
44

5+
## SDK Version Compatibility
6+
7+
This plugin is developed and tested with the following SDK versions:
8+
9+
| Dependency | Version |
10+
|------------|---------|
11+
| MCP SDK (io.modelcontextprotocol.sdk:mcp-bom) | 0.17.0 |
12+
| Spring AI (org.springframework.ai:spring-ai-bom) | 1.1.2 |
13+
| Spring Boot | 3.3.1 |
14+
15+
### Supported Protocols
16+
17+
- **SSE (Server-Sent Events)**: `/sse` endpoint with a long-lived streaming connection and session ID correlation
18+
- **Streamable HTTP**: Unified endpoint handling POST message requests for MCP operations
19+
20+
### Known Limitations
21+
22+
1. **Session Management**: Uses reflection to access SDK internal fields for Session ID retrieval; SDK version upgrades may affect compatibility
23+
2. **Tool Call Timeout**: Default 60 seconds, configurable via `requestTemplate.timeout`
24+
3. **CORS Support**: Configure allowed headers via `shenyu.cross.allowedHeaders`
25+
526
## 1. Simple GET Request Examples
627

728
### 1.1 GET Request with No Parameters
@@ -496,4 +517,3 @@ Implement retry mechanisms and proper error responses at the business layer.
496517
- Monitor and log API performance
497518

498519
These examples are based on **real interfaces from the shenyu-examples-http project** and can be used and tested directly in a Shenyu environment.
499-

shenyu-plugin/shenyu-plugin-mcp-server/pom.xml

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,6 @@
4141
<artifactId>shenyu-web</artifactId>
4242
<version>${project.version}</version>
4343
</dependency>
44-
<!-- <dependency>-->
45-
<!-- <groupId>org.springframework.ai</groupId>-->
46-
<!-- <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>-->
47-
<!-- </dependency>-->
4844
<dependency>
4945
<groupId>org.apache.shenyu</groupId>
5046
<artifactId>shenyu-plugin-base</artifactId>
@@ -55,43 +51,47 @@
5551
<artifactId>shenyu-loadbalancer</artifactId>
5652
<version>${project.version}</version>
5753
</dependency>
54+
55+
<!--
56+
Spring AI and MCP SDK Dependencies
57+
58+
SDK Compatibility Notes:
59+
- Current tested version: MCP SDK 0.17.0, Spring AI 1.1.2
60+
- McpSessionHelper uses reflection to access McpSyncServerExchange.exchange and
61+
McpAsyncServerExchange.session fields for session ID extraction
62+
- ShenyuStreamableHttpServerTransportProvider uses reflection to access
63+
McpServerSession.initialized and McpServerSession.state fields for session verification
64+
- When upgrading SDK versions, verify that these internal fields still exist and
65+
update McpSessionHelper.SUPPORTED_SDK_VERSION and any corresponding
66+
SDK compatibility documentation in ShenyuStreamableHttpServerTransportProvider
67+
-->
5868
<dependency>
5969
<groupId>org.springframework.ai</groupId>
6070
<artifactId>spring-ai-model</artifactId>
6171
</dependency>
62-
72+
6373
<dependency>
6474
<groupId>org.springframework.ai</groupId>
6575
<artifactId>spring-ai-mcp</artifactId>
6676
</dependency>
67-
77+
78+
<!-- MCP SDK - Explicit version from parent BOM -->
6879
<dependency>
6980
<groupId>io.modelcontextprotocol.sdk</groupId>
7081
<artifactId>mcp-spring-webflux</artifactId>
82+
<!-- Version managed by mcp-bom in parent pom.xml (${mcp.version}) -->
7183
</dependency>
7284
<dependency>
7385
<groupId>io.modelcontextprotocol.sdk</groupId>
7486
<artifactId>mcp-json-jackson2</artifactId>
87+
<!-- Version managed by mcp-bom in parent pom.xml (${mcp.version}) -->
7588
</dependency>
76-
<!-- <dependency>-->
77-
<!-- <groupId>org.springframework.ai</groupId>-->
78-
<!-- <artifactId>spring-ai-starter-mcp-server</artifactId>-->
79-
<!-- </dependency>-->
80-
81-
89+
8290
<dependency>
8391
<groupId>org.springframework.boot</groupId>
8492
<artifactId>spring-boot-starter-webflux</artifactId>
8593
</dependency>
86-
87-
<!-- <dependency>-->
88-
<!-- <groupId>org.springframework.boot</groupId>-->
89-
<!-- <artifactId>spring-boot-starter-test</artifactId>-->
90-
<!-- </dependency>-->
91-
<!-- <dependency>-->
92-
<!-- <groupId>org.springframework</groupId>-->
93-
<!-- <artifactId>spring-test</artifactId>-->
94-
<!-- </dependency>-->
94+
9595
<dependency>
9696
<groupId>org.springframework</groupId>
9797
<artifactId>spring-web</artifactId>

shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ public class ShenyuToolCallback implements ToolCallback {
9595
*/
9696
private static final String STREAMABLE_HTTP_PATH = "/streamablehttp";
9797

98+
/**
99+
* Prefix used by McpSessionHelper for SDK compatibility failures.
100+
*/
101+
private static final String SDK_COMPATIBILITY_ERROR_PREFIX = "SDK COMPATIBILITY ERROR";
102+
98103
/**
99104
* Regex pattern for template variable interpolation in tool inputs.
100105
**/
@@ -761,20 +766,35 @@ private McpSyncServerExchange extractMcpExchange(final ToolContext toolContext)
761766
*
762767
* @param mcpExchange the MCP sync server exchange
763768
* @return the session ID
764-
* @throws IllegalStateException if session ID cannot be extracted
769+
* @throws IllegalStateException if the session ID is blank or an SDK compatibility issue blocks extraction
770+
* @throws IllegalArgumentException if the exchange is missing required session state
765771
*/
766772
private String extractSessionId(final McpSyncServerExchange mcpExchange) {
767-
final String sessionId;
768773
try {
769-
sessionId = McpSessionHelper.getSessionId(mcpExchange);
770-
} catch (NoSuchFieldException | IllegalAccessException e) {
771-
throw new RuntimeException(e);
772-
}
773-
if (StringUtils.hasText(sessionId)) {
774-
LOG.debug("Extracted session ID: {}", sessionId);
775-
return sessionId;
774+
final String sessionId = McpSessionHelper.getSessionId(mcpExchange);
775+
if (StringUtils.hasText(sessionId)) {
776+
LOG.debug("Extracted session ID: {}", sessionId);
777+
return sessionId;
778+
}
779+
throw new IllegalStateException("Session ID is empty – it should have been set earlier by handleMessageEndpoint");
780+
} catch (RuntimeException e) {
781+
if (!isSdkCompatibilityError(e)) {
782+
throw e;
783+
}
784+
785+
// Re-throw SDK compatibility errors with additional context.
786+
throw new IllegalStateException(
787+
"Failed to extract session ID from MCP exchange. "
788+
+ "This may indicate an SDK compatibility issue. "
789+
+ "Tested SDK version: " + McpSessionHelper.getSupportedSdkVersion() + ". "
790+
+ "Original error: " + e.getMessage(), e);
776791
}
777-
throw new IllegalStateException("Session ID is empty – it should have been set earlier by handleMessageEndpoint");
792+
}
793+
794+
private boolean isSdkCompatibilityError(final RuntimeException exception) {
795+
return exception instanceof IllegalStateException
796+
&& StringUtils.hasText(exception.getMessage())
797+
&& exception.getMessage().startsWith(SDK_COMPATIBILITY_ERROR_PREFIX);
778798
}
779799

780800
/**

0 commit comments

Comments
 (0)