spring-projects/spring-ai · error · IllegalArgumentException
Sync complete methods should use McpSyncRequestContext inste
Error message
Sync complete methods should use McpSyncRequestContext instead of McpAsyncRequestContext parameter: ${method} in ${declaringClass} What it means
A sync resource method (non-reactive return type) declared an McpAsyncRequestContext parameter. Async request contexts only exist for reactive return types (Mono/Flux); combining a sync return type with an async context is contradictory, so validateParametersWithoutUriVariables throws IllegalArgumentException advising McpSyncRequestContext.
Source
Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/resource/AbstractMcpResourceMethodCallback.java:216
boolean hasRequestOrUriParam = false;
boolean hasMetaParam = false;
boolean hasRequestContextParam = false;
for (Parameter param : parameters) {
// Skip @McpProgressToken annotated parameters
if (param.isAnnotationPresent(McpProgressToken.class)) {
continue;
}
Class<?> paramType = param.getType();
if (McpSyncRequestContext.class.isAssignableFrom(paramType)) {
if (hasRequestContextParam) {
throw new IllegalArgumentException("Method cannot have more than one request context parameter: "
+ method.getName() + " in " + method.getDeclaringClass().getName());
}
if (McpPredicates.isReactiveReturnType.test(method)) {
throw new IllegalArgumentException(
"Sync complete methods should use McpSyncRequestContext instead of McpAsyncRequestContext parameter: "
+ method.getName() + " in " + method.getDeclaringClass().getName());
}
hasRequestContextParam = true;
}
else if (McpAsyncRequestContext.class.isAssignableFrom(paramType)) {
if (hasRequestContextParam) {
throw new IllegalArgumentException("Method cannot have more than one request context parameter: "
+ method.getName() + " in " + method.getDeclaringClass().getName());
}
if (McpPredicates.isNotReactiveReturnType.test(method)) {
throw new IllegalArgumentException(
"Async complete methods should use McpAsyncRequestContext instead of McpSyncRequestContext parameter: "
+ method.getName() + " in " + method.getDeclaringClass().getName());
}
hasRequestContextParam = true;
}
else if (McpMeta.class.isAssignableFrom(paramType)) {View on GitHub (pinned to 98a7beda4f)
Solutions
- Replace McpAsyncRequestContext with McpSyncRequestContext in the method signature.
- Alternatively, make the return type reactive (Mono/Flux) and use the async callback variant if async semantics are needed.
- Align context type and callback type: sync callback + sync context, async callback + async context.
Example fix
// before
@McpResource(uri = "file:///docs/{name}")
public String read(McpAsyncRequestContext ctx, String name) { ... }
// after
@McpResource(uri = "file:///docs/{name}")
public String read(McpSyncRequestContext ctx, String name) { ... } Defensive patterns
Strategy: validation
Validate before calling
static boolean contextMatchesReturnType(Method m) {
boolean reactiveReturn = reactor.core.publisher.Mono.class.isAssignableFrom(m.getReturnType())
|| reactor.core.publisher.Flux.class.isAssignableFrom(m.getReturnType());
boolean hasAsyncCtx = java.util.Arrays.stream(m.getParameterTypes())
.anyMatch(t -> McpAsyncRequestContext.class.isAssignableFrom(t));
return !hasAsyncCtx || reactiveReturn;
} Try / catch
try {
resourceManager.register(callback);
} catch (IllegalArgumentException e) {
throw new IllegalStateException("Sync resource method must use McpSyncRequestContext", e);
} Prevention
- Pair sync return types with McpSyncRequestContext and reactive return types with McpAsyncRequestContext.
- When converting a method from reactive to sync style, swap the context parameter too.
- Add a signature-lint test that checks context type vs return type across all @McpResource methods.
When it happens
Trigger: Declaring @McpResource method with a plain/String/POJO return type but taking McpAsyncRequestContext; McpPredicates.isReactiveReturnType detects the sync return type and the validator throws on the mismatch.
Common situations: Mixing examples from async and sync resource documentation; migrating a method from reactive to sync style and forgetting to swap the context type; IDE auto-complete picking the async context class.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Method cannot have more than one request context parameter:
- Unsupported Async exchange type: ${exchangeType} for Sync me
- Method must not be null
- Method can have at most 2 input parameters (excluding @McpPr
- Method must have exactly 1 parameter (List<McpSchema.Resourc
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/e5cbeaa8e294cb9c.
Report an issue: GitHub.