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

  1. Replace McpAsyncRequestContext with McpSyncRequestContext in the method signature.
  2. Alternatively, make the return type reactive (Mono/Flux) and use the async callback variant if async semantics are needed.
  3. 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

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


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/e5cbeaa8e294cb9c. Report an issue: GitHub.