spring-projects/spring-ai · error · java.lang.IllegalArgumentException
Method cannot have more than one @McpProgressToken parameter
Error message
Method cannot have more than one @McpProgressToken parameter: {method.getName()} in {method.getDeclaringClass().getName()} What it means
validateParameters() walks the @McpPrompt method's parameters and allows at most one parameter annotated with @McpProgressToken; the framework maps the incoming progress token to it. Declaring two or more such parameters is ambiguous and rejected with IllegalArgumentException naming the method and declaring class.
Solutions
- Remove the extra @McpProgressToken annotation so exactly one parameter carries it.
- If both values are needed, capture the token once and pass it to a shared helper instead of injecting it twice.
- Review the method signature and keep only supported parameter kinds (single prompt arg, McpMeta, single context, single @McpProgressToken).
Example fix
// before
@McpPrompt(name = "p")
public String complete(String q, @McpProgressToken String t1, @McpProgressToken String t2) { ... }
// after
@McpPrompt(name = "p")
public String complete(String q, @McpProgressToken String token) { ... } Defensive patterns
Strategy: validation
Validate before calling
long tokenParams = Arrays.stream(method.getParameters())
.filter(p -> p.isAnnotationPresent(McpProgressToken.class)).count();
if (tokenParams > 1) throw new IllegalStateException("Only one @McpProgressToken parameter allowed"); Try / catch
try { registry.register(bean); } catch (IllegalArgumentException e) { log.error("Invalid @McpPrompt signature: {}", e.getMessage()); } Prevention
- Count @McpProgressToken annotations when writing prompt methods
- After copy-pasting parameters, re-read the full signature
- Keep a project convention: one progress token parameter max, injected once and reused
When it happens
Trigger: An @McpPrompt method declaring two parameters annotated @McpProgressToken, e.g. complete(String value, @McpProgressToken String t1, @McpProgressToken String t2).
Common situations: Copy-pasting a parameter block and forgetting to remove the duplicate @McpProgressToken annotation; refactoring that added a second progress-aware parameter; misunderstanding that only one token can be injected per callback.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Method cannot have more than one McpMeta parameter
- Method cannot have more than one request context parameter
- Async complete methods should use McpAsyncRequestContext…
- Async prompt method must not declare parameter of type
- Currently only methods with a single ElicitRequest…
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/6dc06f79e3917a5d.
Report an issue: GitHub.
Appendix: source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/AbstractMcpPromptMethodCallback.java:130
java.lang.reflect.Parameter[] parameters = method.getParameters();
// Check for duplicate parameter types
boolean hasExchangeParam = false;
boolean hasRequestParam = false;
boolean hasMapParam = false;
boolean hasProgressTokenParam = false;
boolean hasMetaParam = false;
boolean hasRequestContextParam = false;
for (java.lang.reflect.Parameter param : parameters) {
Class<?> paramType = param.getType();
this.validateParamType(paramType);
// Skip @McpProgressToken annotated parameters from validation
if (param.isAnnotationPresent(McpProgressToken.class)) {
if (hasProgressTokenParam) {
throw new IllegalArgumentException("Method cannot have more than one @McpProgressToken parameter: "
+ method.getName() + " in " + method.getDeclaringClass().getName());
}
hasProgressTokenParam = true;
continue;
}
// Skip McpMeta parameters from validation
if (McpMeta.class.isAssignableFrom(paramType)) {
if (hasMetaParam) {
throw new IllegalArgumentException("Method cannot have more than one McpMeta parameter: "
+ method.getName() + " in " + method.getDeclaringClass().getName());
}
hasMetaParam = true;
continue;
}
if (McpSyncRequestContext.class.isAssignableFrom(paramType)) {
if (hasRequestContextParam) {View on GitHub (pinned to 98a7beda4f)