Skip to content

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

Closed
@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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions