Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
zekozhang opened this issue Feb 18, 2025 · 1 comment

Comments

@zekozhang
Copy link

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

@zekozhang
Copy link
Author

Fixed. modelcontextprotocol/java-sdk#21

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant