{"record":{"id":"d564c9b4598a28ac","repo":"spring-projects/spring-ai","slug":"method-cannot-have-more-than-one-request-context-p-d564c9","errorCode":null,"errorMessage":"Method cannot have more than one request context parameter: {method.getName()} in {method.getDeclaringClass().getName()}","messagePattern":"Method cannot have more than one request context parameter: (.+?) in (.+?)","errorType":"validation","errorClass":"java.lang.IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/AbstractMcpPromptMethodCallback.java","lineNumber":149,"sourceCode":"\t\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName());\n\t\t\t\t}\n\t\t\t\thasProgressTokenParam = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip McpMeta parameters from validation\n\t\t\tif (McpMeta.class.isAssignableFrom(paramType)) {\n\t\t\t\tif (hasMetaParam) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Method cannot have more than one McpMeta parameter: \"\n\t\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName());\n\t\t\t\t}\n\t\t\t\thasMetaParam = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (McpSyncRequestContext.class.isAssignableFrom(paramType)) {\n\t\t\t\tif (hasRequestContextParam) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Method cannot have more than one request context parameter: \"\n\t\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName());\n\t\t\t\t}\n\t\t\t\tif (McpPredicates.isReactiveReturnType.test(method)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\"Sync complete methods should use McpSyncRequestContext instead of McpAsyncRequestContext parameter: \"\n\t\t\t\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName());\n\t\t\t\t}\n\t\t\t\thasRequestContextParam = true;\n\t\t\t}\n\t\t\telse if (McpAsyncRequestContext.class.isAssignableFrom(paramType)) {\n\t\t\t\tif (hasRequestContextParam) {\n\t\t\t\t\tthrow new IllegalArgumentException(\"Method cannot have more than one request context parameter: \"\n\t\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName());\n\t\t\t\t}\n\t\t\t\tif (McpPredicates.isNotReactiveReturnType.test(method)) {\n\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\"Async complete methods should use McpAsyncRequestContext instead of McpSyncRequestContext parameter: \"\n\t\t\t\t\t\t\t\t\t+ method.getName() + \" in \" + method.getDeclaringClass().getName());","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/AbstractMcpPromptMethodCallback.java#L131-L167","documentation":"validateParameters() enforces that an @McpPrompt method has at most one request-context parameter. When a parameter is assignable from McpSyncRequestContext, declaring a second context parameter (sync or async) is ambiguous and throws IllegalArgumentException naming the method and declaring class.","triggerScenarios":"An @McpPrompt method declaring two context parameters, e.g. complete(String q, McpSyncRequestContext c1, McpSyncRequestContext c2), or mixing McpSyncRequestContext with McpAsyncRequestContext.","commonSituations":"Copy-paste duplication of a context parameter; refactoring from sync to async by adding the async context without removing the sync one; confusion over which context type the framework injects.","solutions":["Remove the duplicate context parameter, keeping exactly one McpSyncRequestContext.","If you need async behavior, remove the sync context entirely and use a single McpAsyncRequestContext with a reactive return type.","Re-check the full parameter list after refactoring to confirm only one context parameter remains."],"exampleFix":"// before\n@McpPrompt(name = \"p\")\npublic String complete(String q, McpSyncRequestContext c1, McpSyncRequestContext c2) { ... }\n// after\n@McpPrompt(name = \"p\")\npublic String complete(String q, McpSyncRequestContext context) { ... }","handlingStrategy":"validation","validationCode":"long ctxParams = Arrays.stream(method.getParameters())\n    .filter(p -> McpSyncRequestContext.class.isAssignableFrom(p.getType())\n              || McpAsyncRequestContext.class.isAssignableFrom(p.getType())).count();\nif (ctxParams > 1) throw new IllegalStateException(\"Only one request context parameter allowed\");","typeGuard":null,"tryCatchPattern":"try { registry.register(bean); } catch (IllegalArgumentException e) { log.error(\"Invalid @McpPrompt signature: {}\", e.getMessage()); }","preventionTips":["Always exactly one context parameter per prompt method","When migrating sync→async, replace the context rather than adding one","Adopt a signature template: (prompt arg, optional McpMeta, one context, optional @McpProgressToken)"],"tags":["java","mcp","annotation","method-signature"],"backgroundTag":"invalid-argument-value","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}