spring-projects/spring-ai · warning

Stateless servers doesn't support bidirectional parameters.

Error message

Stateless servers doesn't support bidirectional parameters. Skipping method <method> with bidirectional parameters

What it means

Stateless MCP servers cannot correlate the client callback that bidirectional parameters require. When a method on a stateless server declares parameters that support bidirectional communication (e.g. McpSyncRequestContext/McpAsyncRequestContext exchange with callbacks), the method is skipped and a warning is logged rather than thrown.

Source

Thrown at mcp/mcp-annotations/src/main/java/org/springframework/ai/mcp/annotation/common/McpPredicates.java:102

			if (McpSyncRequestContext.class.isAssignableFrom(paramType)
					|| McpAsyncRequestContext.class.isAssignableFrom(paramType)
					|| McpSyncServerExchange.class.isAssignableFrom(paramType)
					|| McpAsyncServerExchange.class.isAssignableFrom(paramType)) {

				return true;
			}
		}

		return false;
	}

	public static Predicate<Method> filterMethodWithBidirectionalParameters() {
		return method -> {
			if (!hasBidirectionalParameters(method)) {
				return true;
			}
			if (logger.isWarnEnabled()) {
				logger.warn("Stateless servers doesn't support bidirectional parameters. Skipping method " + method
						+ " with bidirectional parameters");
			}
			return false;
		};
	}

}

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Use a stateful server builder (McpServer.async()/sync() stateful variant) so bidirectional parameters are supported
  2. Remove the bidirectional context parameter from the method, or restrict the method to stateless-safe operations (no sampling/elicitation/progress)
  3. Keep bidirectional methods in a separate service registered only with stateful servers

Example fix

// before (stateless server)
@McpTool(description = "Ask")
public String ask(McpSyncRequestContext ctx, String q) { return ctx.sample(s -> s.prompt("Think: " + q)).block(); }

// after (stateless-safe)
@McpTool(description = "Ask")
public String ask(String q) { return staticAnswer(q); }
Defensive patterns

Strategy: validation

Validate before calling

boolean stateless = builder instanceof McpServer.StatelessSyncSpecification || builder instanceof McpServer.StatelessAsyncSpecification;
if (stateless) {
  for (Method m : service.getClass().getDeclaredMethods()) {
    for (Class<?> p : m.getParameterTypes()) {
      if (McpSyncRequestContext.class == p || McpAsyncRequestContext.class == p) {
        throw new IllegalStateException(m + " uses bidirectional context; needs a stateful server");
      }
    }
  }
}

Type guard

static boolean usesBidirectionalContext(Method m) {
  return java.util.Arrays.stream(m.getParameterTypes())
    .anyMatch(p -> p == McpSyncRequestContext.class || p == McpAsyncRequestContext.class);
}

Prevention

When it happens

Trigger: Declaring a method parameter of type McpSyncRequestContext (or the async equivalent) that performs bidirectional operations like sampling or elicitation, then registering it with a STATELESS MCP server.

Common situations: Configuring a stateless (McpServer.StatelessAsyncSpecification/StatelessSyncSpecification) server while copying tool examples that use request contexts for sampling/progress; stateful-to-stateless migration.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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