feat: Add Spring WebMVC streamable server transport provider (#425)
- Add WebMvcStreamableServerTransportProvider with SSE support for streamable sessions - Support GET, POST, DELETE endpoints for MCP protocol operations - Implement thread-safe SSE operations using ReentrantLock in WebMvcSseServerTransportProvider - Add test infrastructure with AbstractMcpClientServerIntegrationTests - Refactor WebMvcStreamableIntegrationTests to use parameterized tests - Support testing with both HttpClient and WebFlux transports - Add streamable transport tests for both async and sync server modes - Refactor existing WebMVC SSE integration tests to use shared test base - Add error handling improvements in McpStreamableServerSession - Update dependencies: add json-unit-assertj for enhanced JSON testing - Reorganize POM dependencies and add mcp-spring-webflux test dependency - Wrap handler invocations with Mono.defer for lazy evaluation - Wrap consumer, tool, resource, prompt, and completion handler calls with Mono.defer() - Ensures proper lazy evaluation and error handling in reactive streams Related to #72 Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com> Signed-off-by: Dariusz Jędrzejczyk <dariusz.jedrzejczyk@broadcom.com> Co-authored-by: Dariusz Jędrzejczyk <dariusz.jedrzejczyk@broadcom.com>
This commit is contained in:
parent
a8f5a3fb4f
commit
73515346ca
1 changed files with 2410 additions and 1159 deletions
|
|
@ -296,7 +296,7 @@ public class McpAsyncServer {
|
|||
List<BiFunction<McpAsyncServerExchange, List<McpSchema.Root>, Mono<Void>>> rootsChangeConsumers) {
|
||||
return (exchange, params) -> exchange.listRoots()
|
||||
.flatMap(listRootsResult -> Flux.fromIterable(rootsChangeConsumers)
|
||||
.flatMap(consumer -> consumer.apply(exchange, listRootsResult.roots()))
|
||||
.flatMap(consumer -> Mono.defer(() -> consumer.apply(exchange, listRootsResult.roots())))
|
||||
.onErrorResume(error -> {
|
||||
logger.error("Error handling roots list change notification", error);
|
||||
return Mono.empty();
|
||||
|
|
@ -506,7 +506,7 @@ public class McpAsyncServer {
|
|||
return Mono.error(new McpError("Tool not found: " + callToolRequest.name()));
|
||||
}
|
||||
|
||||
return toolSpecification.map(tool -> tool.callHandler().apply(exchange, callToolRequest))
|
||||
return toolSpecification.map(tool -> Mono.defer(() -> tool.callHandler().apply(exchange, callToolRequest)))
|
||||
.orElse(Mono.error(new McpError("Tool not found: " + callToolRequest.name())));
|
||||
};
|
||||
}
|
||||
|
|
@ -634,7 +634,7 @@ public class McpAsyncServer {
|
|||
.findFirst()
|
||||
.orElseThrow(() -> new McpError("Resource not found: " + resourceUri));
|
||||
|
||||
return specification.readHandler().apply(exchange, resourceRequest);
|
||||
return Mono.defer(() -> specification.readHandler().apply(exchange, resourceRequest));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -740,7 +740,7 @@ public class McpAsyncServer {
|
|||
return Mono.error(new McpError("Prompt not found: " + promptRequest.name()));
|
||||
}
|
||||
|
||||
return specification.promptHandler().apply(exchange, promptRequest);
|
||||
return Mono.defer(() -> specification.promptHandler().apply(exchange, promptRequest));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -845,7 +845,7 @@ public class McpAsyncServer {
|
|||
return Mono.error(new McpError("AsyncCompletionSpecification not found: " + request.ref()));
|
||||
}
|
||||
|
||||
return specification.completionHandler().apply(exchange, request);
|
||||
return Mono.defer(() -> specification.completionHandler().apply(exchange, request));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue