apache/shenyu · error · IllegalArgumentException
McpSyncServerExchange is required in ToolContext
Error message
McpSyncServerExchange is required in ToolContext
What it means
IllegalArgumentException from McpSessionHelper.getMcpSyncServerExchange when the ToolContext has a non-empty map but no value under the key 'exchange'. The helper expects the MCP tool execution pipeline to place the current McpSyncServerExchange in the context; its absence means the exchange was never stored or stored under a different key.
Solutions
- Ensure the McpSyncServerExchange is stored under the exact key "exchange" in the ToolContext map
- Route tool calls through the standard ShenYu MCP server execution path so the framework sets the key itself
- If you construct the context yourself, add Map.of("exchange", mcpSyncServerExchange)
- Check that no code overwrites or clears the 'exchange' entry before the callback runs
Example fix
// before
new ToolContext(Map.of("sessionId", sessionId))
// after
new ToolContext(Map.of("sessionId", sessionId, "exchange", mcpSyncServerExchange)) Defensive patterns
Strategy: validation
Validate before calling
Object ex = toolContext.getContext().get("exchange");
if (!(ex instanceof McpSyncServerExchange)) {
throw new IllegalArgumentException("ToolContext map must contain key \"exchange\" -> McpSyncServerExchange");
} Type guard
boolean hasMcpExchange(ToolContext ctx) {
return ctx != null && ctx.getContext() != null
&& ctx.getContext().get("exchange") instanceof McpSyncServerExchange;
} Try / catch
try {
McpSessionHelper.getMcpSyncServerExchange(toolContext);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("McpSyncServerExchange is required in ToolContext")) {
LOG.error("ToolContext missing \"exchange\" key; expected McpSyncServerExchange", e);
} else throw e;
} Prevention
- Store the exchange under the exact key "exchange"
- Do not overwrite/clear the key in custom middleware
- Check the key contract after any Spring AI/MCP SDK upgrade
When it happens
Trigger: contextMap.get("exchange") returns null during getMcpSyncServerExchange — the ToolContext map contains other entries but no 'exchange' key, or the value stored was null / of a different type (which then also fails the cast).
Common situations: Custom tool-invocation code putting the exchange under a different key name; constructing ToolContext manually in tests with partial context; an MCP SDK / Spring AI upgrade changing the context key contract.
Related errors
- Import mcp server config failed:
- OpenAPI document is missing the top-level 'servers' field…
- OpenAPI pathKey cannot be null or empty
- OpenAPI methodType cannot be null or empty
- Tool execution failed
AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12).
Data as JSON: /api/errors/64b115760e07790b.
Report an issue: GitHub.
Appendix: source
Thrown at shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/session/McpSessionHelper.java:140
}
/**
* Get McpSyncServerExchange from ToolContext.
*
* @param toolContext the tool context
* @return the McpSyncServerExchange instance
*/
public static McpSyncServerExchange getMcpSyncServerExchange(final ToolContext toolContext) {
if (Objects.isNull(toolContext)) {
throw new IllegalArgumentException("ToolContext is required");
}
Map<String, Object> contextMap = toolContext.getContext();
if (Objects.isNull(contextMap) || contextMap.isEmpty()) {
throw new IllegalArgumentException("ToolContext is required");
}
McpSyncServerExchange mcpSyncServerExchange = (McpSyncServerExchange) contextMap.get("exchange");
if (Objects.isNull(mcpSyncServerExchange)) {
throw new IllegalArgumentException("McpSyncServerExchange is required in ToolContext");
}
return mcpSyncServerExchange;
}
/**
* Get sessionId from McpSyncServerExchange.
*
* <p>Uses reflection to access internal SDK fields. If reflection fails,
* an IllegalStateException is thrown with SDK compatibility information.
*
* @param mcpSyncServerExchange the McpSyncServerExchange instance
* @return the session id string
* @throws IllegalStateException if SDK reflection fails (API incompatibility)
*/
public static String getSessionId(final McpSyncServerExchange mcpSyncServerExchange) {
return getSession(mcpSyncServerExchange).getId();
}
View on GitHub (pinned to 567142e072)