{"record":{"id":"53aaf077ba621d47","repo":"spring-projects/spring-ai","slug":"stateless-tool-methods-do-not-support-mcpsyncreque","errorCode":null,"errorMessage":"Stateless tool methods do not support McpSyncRequestContext parameter.","messagePattern":"Stateless tool methods do not support McpSyncRequestContext parameter\\.","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/tool/SyncStatelessMcpToolMethodCallback.java","lineNumber":69,"sourceCode":"\t * The {@code toolCallExceptionClass} argument is ignored: exception handling now\n\t * follows the {@code @Tool} contract based on the exception type. Will be removed in\n\t * 2.1.0.\n\t */\n\t@Deprecated\n\tpublic SyncStatelessMcpToolMethodCallback(ReturnMode returnMode, java.lang.reflect.Method toolMethod,\n\t\t\tObject toolObject, Class<? extends Throwable> toolCallExceptionClass) {\n\t\tsuper(returnMode, toolMethod, toolObject, toolCallExceptionClass);\n\t}\n\n\t@Override\n\tprotected boolean isExchangeOrContextType(Class<?> paramType) {\n\t\treturn McpTransportContext.class.isAssignableFrom(paramType)\n\t\t\t\t|| McpSyncRequestContext.class.isAssignableFrom(paramType);\n\t}\n\n\t@Override\n\tprotected McpSyncRequestContext createRequestContext(McpTransportContext exchange, CallToolRequest request) {\n\t\tthrow new UnsupportedOperationException(\n\t\t\t\t\"Stateless tool methods do not support McpSyncRequestContext parameter.\");\n\t}\n\n\t@Override\n\tprotected McpTransportContext resolveTransportContext(McpTransportContext context) {\n\t\treturn context;\n\t}\n\n\t@Override\n\tpublic CallToolResult apply(McpTransportContext mcpTransportContext, CallToolRequest callToolRequest) {\n\t\tvalidateSyncRequest(callToolRequest);\n\n\t\ttry {\n\t\t\t// Build arguments for the method call\n\t\t\tObject[] args = this.buildMethodArguments(mcpTransportContext, callToolRequest.arguments(),\n\t\t\t\t\tcallToolRequest);\n\n\t\t\t// Invoke the method","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/tool/SyncStatelessMcpToolMethodCallback.java#L51-L87","documentation":"SyncStatelessMcpToolMethodCallback serves @McpTool methods on a stateless sync client. Since stateless callbacks have no active MCP exchange, McpSyncRequestContext cannot be instantiated; createRequestContext always throws UnsupportedOperationException. Methods may only declare McpTransportContext (or no context) parameters.","triggerScenarios":"Declaring a @McpTool method with a McpSyncRequestContext parameter (e.g. @McpTool(name=\"x\") String tool(String arg, McpSyncRequestContext ctx)) and registering it with a stateless sync tool callback, then calling it.","commonSituations":"Copy-pasting a stateful tool method into a stateless configuration; tutorials mixing stateful examples with stateless client setup; refactoring that changed the registry type but not method signatures.","solutions":["Drop the McpSyncRequestContext parameter from the tool method signature.","Use McpTransportContext as the parameter if some context is needed — stateless callbacks resolve and pass it.","Switch the registration to the stateful sync callback (SyncMcpToolMethodCallback) if exchange-aware elicitation/progress features are required."],"exampleFix":"// before\n@McpTool(name = \"search\")\nString search(String query, McpSyncRequestContext ctx) { ... }\n\n// after\n@McpTool(name = \"search\")\nString search(String query) { ... }","handlingStrategy":"validation","validationCode":"// Fail fast at startup if a stateless sync tool method declares McpSyncRequestContext\nfor (Method m : bean.getClass().getDeclaredMethods()) {\n    if (m.isAnnotationPresent(McpTool.class)) {\n        for (Class<?> p : m.getParameterTypes()) {\n            if (McpSyncRequestContext.class.isAssignableFrom(p)) {\n                throw new IllegalStateException(\"@McpTool \" + m.getName() + \" cannot take McpSyncRequestContext in a stateless callback\");\n            }\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never declare McpSyncRequestContext parameters in stateless tool methods.","Use McpTransportContext as the context parameter instead.","When migrating between stateful/stateless setups, audit all @McpTool signatures."],"tags":["unsupported-operation","mcp","stateless","sync"],"backgroundTag":"unsupported-operation","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}