{"record":{"id":"581339ded2c87eb1","repo":"apache/shenyu","slug":"failed-to-retrieve-mcp-sync-server-exchange-from-context","errorCode":null,"errorMessage":"Failed to retrieve MCP sync server exchange from context","messagePattern":"Failed to retrieve MCP sync server exchange from context","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java","lineNumber":759,"sourceCode":"     * @return true if the method supports request body\n     */\n    private boolean isRequestBodyMethod(final String method) {\n        return \"POST\".equalsIgnoreCase(method)\n                || \"PUT\".equalsIgnoreCase(method)\n                || \"PATCH\".equalsIgnoreCase(method);\n    }\n\n    /**\n     * Extracts the MCP sync server exchange from the tool context.\n     *\n     * @param toolContext the tool context containing MCP session information\n     * @return the MCP sync server exchange\n     * @throws IllegalStateException if exchange cannot be retrieved\n     */\n    private McpSyncServerExchange extractMcpExchange(final ToolContext toolContext) {\n        final McpSyncServerExchange exchange = McpSessionHelper.getMcpSyncServerExchange(toolContext);\n        if (Objects.isNull(exchange)) {\n            throw new IllegalStateException(\"Failed to retrieve MCP sync server exchange from context\");\n        }\n        return exchange;\n    }\n\n    /**\n     * Extracts the session ID from the MCP sync server exchange.\n     *\n     * @param mcpExchange the MCP sync server exchange\n     * @return the session ID\n     * @throws IllegalStateException if the session ID is blank or an SDK compatibility issue blocks extraction\n     * @throws IllegalArgumentException if the exchange is missing required session state\n     */\n    private String extractSessionId(final McpSyncServerExchange mcpExchange) {\n        try {\n            final String sessionId = McpSessionHelper.getSessionId(mcpExchange);\n            if (StringUtils.hasText(sessionId)) {\n                LOG.debug(\"Extracted session ID: {}\", sessionId);\n                return sessionId;","sourceCodeStart":741,"sourceCodeEnd":777,"githubUrl":"https://github.com/apache/shenyu/blob/567142e07261b3e615ae8850b30f4421f455cc5d/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/callback/ShenyuToolCallback.java#L741-L777","documentation":"extractMcpExchange fetches the McpSyncServerExchange from the ToolContext via McpSessionHelper and throws IllegalStateException when it is absent. The ShenYu MCP bridge needs the current MCP exchange to build the decorated ServerWebExchange; its absence means the callback was invoked outside a proper MCP session context.","triggerScenarios":"Calling the tool callback with a ToolContext that has no McpSyncServerExchange stored (McpSessionHelper returns null) — e.g. the callback invoked directly in tests, or by a non-MCP caller, or the session state was cleared before the call.","commonSituations":"Unit-testing ShenyuToolCallback with a synthetic ToolContext; MCP client disconnecting/timing out so the session was removed before tool execution; invoking the callback through a path that bypasses ShenyuMcpExchangeHolder session setup.","solutions":["Ensure the tool is invoked through the MCP server flow that populates the exchange into the ToolContext/session holder first.","In tests, register a mock McpSyncServerExchange via McpSessionHelper/McpSessionHelper-equivalent setup before calling.","Check session lifetime configuration — if sessions expire quickly, increase the timeout or reuse a persistent session.","Confirm the MCP client maintains the same sessionId across initialization and tool calls."],"exampleFix":"// before (test)\ncallback.call(args, new ToolContext(Map.of()));\n// after\nToolContext ctx = McpTestSupport.contextWithExchange(mockMcpSyncServerExchange);\ncallback.call(args, ctx);","handlingStrategy":"type-guard","validationCode":"if (McpSessionHelper.getMcpSyncServerExchange(toolContext) == null) {\n    throw new IllegalStateException(\"No MCP exchange in ToolContext; invoke via MCP server flow\");\n}","typeGuard":"boolean hasMcpExchange(ToolContext ctx) {\n    return McpSessionHelper.getMcpSyncServerExchange(ctx) != null;\n}","tryCatchPattern":"try {\n    return callback.call(args, ctx);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"MCP sync server exchange\")) {\n        // reinitialize the MCP session and retry once, or report session expired\n    }\n    throw e;\n}","preventionTips":["Only invoke ShenyuToolCallback through the MCP server dispatch path that populates the exchange.","In tests, seed the ToolContext with a mock McpSyncServerExchange before calling.","Keep MCP sessions alive long enough to cover tool execution; watch for premature expiry.","Ensure the client reuses one sessionId across initialize and tools/call requests."],"tags":["mcp","session","state","ai"],"backgroundTag":"missing-required-argument","analyzedSha":"567142e07261b3e615ae8850b30f4421f455cc5d","analyzedAt":"2026-09-12T10:08:21.293Z","contentChangedAt":"2026-09-12T10:08:21.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}