Skip to content

Failed to send message to session xxxxxxx: Cannot invoke "org.apache.catalina.connector.OutputBuffer.isBlocking()" because "this.ob" is null #2267

@zekozhang

Description

@zekozhang

I built an MCP Server using spring-ai-mcp-server-webmvc-spring-boot-starter:1.0.0-SNAPSHOT. It can connect normally through a custom MCP Client or MCP Inspector, get tools, and call tool logic by integrating with ChatGPT (spring-ai-openai-spring-boot-starter:1.0.0-M5). However, after calling a tool, an error occurs(sometimes it succeeds, sometimes it fails, but it fails most of the time.):

Image

Failed to send message to session 012dcdf9-1654-4d08-873c-9d20ac112fbf: Cannot invoke "org.apache.catalina.connector.OutputBuffer.isBlocking()" because "this.ob" is null.

But when I implement the MCP Server using spring-boot-starter-webflux, this issue does not occur.

Server core

@Configuration
public class McpWebServerConfig {

	private static final Logger logger = LoggerFactory.getLogger(McpWebServerConfig.class);

	private final ApiClient apiClient;

	private final S2SAccessTokenGenerator tokenGenerator;

	public McpWebServerConfig(ApiClient apiClient, S2SAccessTokenGenerator tokenGenerator) {
		this.apiClient = apiClient;
		this.tokenGenerator = tokenGenerator;
	}

	@Bean
	public List<McpServerFeatures.SyncToolRegistration> settingsToolRegistrations() {
		var userSettings = new McpServerFeatures.SyncToolRegistration(new McpSchema.Tool("userSettings",
				"Retrieve a user's settings. For user-level apps, pass the me value instead of the userId parameter.",
				"{\"type\":\"object\",\"properties\":{\"userId\":{\"type\":\"string\",\"description\":\"The user ID or email address of the user. For user-level apps, pass the `me` value.\"}},\"required\":[\"userId\"]}"),
				(args) -> {
					logger.info("Get user settings request: {}\"", args);
					Object result = apiClient.get()
						.uri("/users/{userId}/settings", args.get("userId"))
						.header("Authorization", "Bearer " + tokenGenerator.generateAccessToken())
						.retrieve()
						.body(Object.class);
					try {
                                                logger.info("Get user settings success: {}\"", new ObjectMapper().writeValueAsString(result));
						return new McpSchema.CallToolResult(List.of(new McpSchema.TextContent(
								"Get user settings successfully, " + new ObjectMapper().writeValueAsString(result))),
								false);
					}
					catch (JsonProcessingException e) {
						throw new RuntimeException(e);
					}
				});

		return List.of(userSettings);
	}
}

Maven dependences

Image

Client core

@Configuration
@EnableConfigurationProperties(McpServerConfig.class)
public class McpClientConfig {

	private static final Logger logger = LoggerFactory.getLogger(McpClientConfig.class);

	private final McpServerConfig mcpServerConfig;

	public McpClientConfig(McpServerConfig mcpServerConfig) {
		this.mcpServerConfig = mcpServerConfig;
	}

	@Bean
	public List<McpFunctionCallback> functionCallbacks(McpSyncClient mcpClient) {

		var callbacks = mcpClient.listTools(null)
			.tools()
			.stream()
			.map(tool -> new McpFunctionCallback(mcpClient, tool))
			.toList();
		return callbacks;
	}

	@Bean(destroyMethod = "close")
	public McpSyncClient mcpClient() {
		
		var mcpClient = McpClient
			.sync(new HttpClientSseClientTransport(mcpServerConfig.getUrl()))
			.requestTimeout(Duration.ofSeconds(60))
			.build();

		var init = mcpClient.initialize();

		logger.info("MCP Initialized: {}", init);

		return mcpClient;

	}
}

**ChatContoller**

private final ChatClient.Builder chatClientBuilder;

private final List<McpFunctionCallback> functionCallbacks;

public ChatController(ChatClient.Builder chatClientBuilder,
		List<McpFunctionCallback> functionCallbacks) {
	this.chatClientBuilder = chatClientBuilder;
	this.functionCallbacks = functionCallbacks;
}

@PostMapping("/chat")
	public Object question(@RequestBody ChatMessage chatMessage) {
		var chatClient = chatClientBuilder.defaultFunctions(functionCallbacks.toArray(new McpFunctionCallback[0]))
			.build();

		logger.info("Communicate with AI question: {}", chatMessage.getMessage());
		ChatClient.CallResponseSpec response = chatClient.prompt(chatMessage.getMessage()).call();
		String responseContent = response.content();

		logger.info("Get AI response: {}", responseContent);
		return responseContent;
	}

Client dependences

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions