{"record":{"id":"4c5c11ff867548f7","repo":"apache/shenyu","slug":"toolcontext-is-required","errorCode":null,"errorMessage":"ToolContext is required","messagePattern":"ToolContext is required","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/session/McpSessionHelper.java","lineNumber":132,"sourceCode":"                    + \"(tested: {}). Error: {}\", SUPPORTED_SDK_VERSION, e.getMessage(), e);\n        }\n    }\n\n    private static void clearReflectionFieldCache() {\n        asyncExchangeFieldCache = null;\n        sessionFieldCache = null;\n        fieldsResolved = false;\n    }\n\n    /**\n     * Get McpSyncServerExchange from ToolContext.\n     *\n     * @param toolContext the tool context\n     * @return the McpSyncServerExchange instance\n     */\n    public static McpSyncServerExchange getMcpSyncServerExchange(final ToolContext toolContext) {\n        if (Objects.isNull(toolContext)) {\n            throw new IllegalArgumentException(\"ToolContext is required\");\n        }\n        Map<String, Object> contextMap = toolContext.getContext();\n        if (Objects.isNull(contextMap) || contextMap.isEmpty()) {\n            throw new IllegalArgumentException(\"ToolContext is required\");\n        }\n        McpSyncServerExchange mcpSyncServerExchange = (McpSyncServerExchange) contextMap.get(\"exchange\");\n        if (Objects.isNull(mcpSyncServerExchange)) {\n            throw new IllegalArgumentException(\"McpSyncServerExchange is required in ToolContext\");\n        }\n        return mcpSyncServerExchange;\n    }\n\n    /**\n     * Get sessionId from McpSyncServerExchange.\n     *\n     * <p>Uses reflection to access internal SDK fields. If reflection fails,\n     * an IllegalStateException is thrown with SDK compatibility information.\n     *","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/apache/shenyu/blob/567142e07261b3e615ae8850b30f4421f455cc5d/shenyu-plugin/shenyu-plugin-mcp-server/src/main/java/org/apache/shenyu/plugin/mcp/server/session/McpSessionHelper.java#L114-L150","documentation":"IllegalArgumentException from McpSessionHelper.getMcpSyncServerExchange when the ToolContext argument is null. Spring AI passes a ToolContext to tool callbacks during MCP tool execution; the helper needs it to retrieve the McpSyncServerExchange under the 'exchange' key. A null context means the tool was invoked without the MCP-provided context.","triggerScenarios":"getMcpSyncServerExchange(null) is called — i.e. a ShenyuToolCallback is executed with a null ToolContext (e.g. calling call()/apply() directly without context, or a Spring AI version not passing context).","commonSituations":"Directly invoking ShenyuToolCallback in tests or custom code without a ToolContext; an older Spring AI runtime that doesn't propagate ToolContext into tool calls; a custom tool-execution wrapper dropping the context argument.","solutions":["Invoke the tool through the MCP server path (or pass a ToolContext built with the 'exchange' key) rather than calling call(args) directly","Upgrade Spring AI to the tested version (1.1.2) so ToolContext is populated automatically for MCP tool calls","In custom wrappers, always forward the ToolContext argument to the underlying callback","Guard in your code: reject tool invocations where ToolContext is null before reaching the plugin"],"exampleFix":"// before\ntoolCallback.call(toolArgs);\n// after\ntoolCallback.call(toolArgs, new ToolContext(Map.of(\"exchange\", mcpSyncServerExchange)));","handlingStrategy":"type-guard","validationCode":"if (toolContext == null) {\n    throw new IllegalArgumentException(\"tool call requires a ToolContext from the MCP pipeline\");\n}","typeGuard":"boolean hasToolContext(Object ctx) { return ctx instanceof ToolContext; }","tryCatchPattern":"try {\n    result = toolCallback.call(args, toolContext);\n} catch (IllegalArgumentException e) {\n    if (\"ToolContext is required\".equals(e.getMessage())) {\n        LOG.error(\"Tool invoked without MCP ToolContext; use the MCP execution path\", e);\n    } else throw e;\n}","preventionTips":["Never call ShenyuToolCallback.call(args) without a ToolContext in custom code","Upgrade Spring AI to 1.1.2 so ToolContext is auto-propagated","Unit-test tool callbacks with a populated ToolContext"],"tags":["mcp","null","argument"],"backgroundTag":"null-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"}