spring-projects/spring-ai · error · java.lang.IllegalArgumentException

Sync prompt method must not declare parameter of type: {para

Error message

Sync prompt method must not declare parameter of type: {paramType.getName()}. Use McpSyncServerExchange instead. Method: {this.method.getName()} in {this.method.getDeclaringClass().getName()}

What it means

Sync prompt methods may receive an McpSyncServerExchange but not an McpAsyncServerExchange, since a synchronous server cannot provide the async exchange. SyncMcpPromptMethodCallback validates each parameter type at registration and throws IllegalArgumentException if it finds the async exchange type.

Source

Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/method/prompt/SyncMcpPromptMethodCallback.java:55

 *
 * This class provides a way to convert methods annotated with {@link McpPrompt} into
 * callback functions that can be used to handle prompt requests. It supports various
 * method signatures and return types.
 *
 * @author Christian Tzolov
 */
public final class SyncMcpPromptMethodCallback extends AbstractMcpPromptMethodCallback
		implements BiFunction<McpSyncServerExchange, GetPromptRequest, GetPromptResult> {

	private SyncMcpPromptMethodCallback(Builder builder) {
		super(builder.method, builder.bean, builder.prompt);
	}

	@Override
	protected void validateParamType(Class<?> paramType) {

		if (McpAsyncServerExchange.class.isAssignableFrom(paramType)) {
			throw new IllegalArgumentException("Sync prompt method must not declare parameter of type: "
					+ paramType.getName() + ". Use McpSyncServerExchange instead." + " Method: " + this.method.getName()
					+ " in " + this.method.getDeclaringClass().getName());
		}
	}

	@Override
	protected Object assignExchangeType(Class<?> paramType, Object exchange) {

		if (McpTransportContext.class.isAssignableFrom(paramType)) {
			if (exchange instanceof McpTransportContext transportContext) {
				return transportContext;
			}
			else if (exchange instanceof McpSyncServerExchange syncServerExchange) {
				return syncServerExchange.transportContext();
			}
			else if (exchange instanceof McpAsyncServerExchange asyncServerExchange) {
				throw new IllegalArgumentException("Unsupported Async exchange type: "
						+ asyncServerExchange.getClass().getName() + " for Sync method: " + method.getName() + " in "

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Change the parameter type to McpSyncServerExchange
  2. Remove the exchange parameter if not needed
  3. Register the bean with the async server (AsyncMcpPromptMethodCallback) if the method must stay async

Example fix

// before
public GetPromptResult p(McpAsyncServerExchange exchange, Request req) {...}
// after
public GetPromptResult p(McpSyncServerExchange exchange, Request req) {...}
Defensive patterns

Strategy: validation

Validate before calling

boolean syncSafe(Method m) { return Arrays.stream(m.getParameterTypes()).noneMatch(t -> McpAsyncServerExchange.class.isAssignableFrom(t)); }

Try / catch

try { syncServer.addPrompt(cb); } catch (IllegalArgumentException e) { log.error("Sync method misconfigured: {}", e.getMessage()); }

Prevention

When it happens

Trigger: Declaring an @McpPrompt method with an McpAsyncServerExchange parameter while registering it with SyncMcpPromptMethodCallback (sync MCP server).

Common situations: Copy-pasting a method signature from async MCP examples into a sync server setup; converting an app from async to sync without changing signatures.

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/853b9fa226c06ba6. Report an issue: GitHub.