Skip to content

Commit 03271f1

Browse files
committed
feat: search implementation
1 parent 51fe594 commit 03271f1

File tree

8 files changed

+727
-18
lines changed

8 files changed

+727
-18
lines changed

mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/WebFluxSseIntegrationTests.java

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,39 @@ void testToolCallSuccess(String clientType) {
559559
mcpServer.close();
560560
}
561561

562+
@ParameterizedTest(name = "{0} : {displayName} ")
563+
@ValueSource(strings = { "httpclient", "webflux" })
564+
void testSearchToolsSuccess(String clientType) {
565+
566+
var clientBuilder = clientBuilders.get(clientType);
567+
568+
var tool1Spec = new McpSchema.Tool("tool1", "tool1 description", emptyJsonSchema);
569+
var searchResponse = McpSchema.SearchToolsResult.builder().tools(List.of(tool1Spec)).build();
570+
McpServerFeatures.SyncToolSpecification tool1 = new McpServerFeatures.SyncToolSpecification(tool1Spec,
571+
(exchange, request) -> CallToolResult.builder().textContent(List.of("CALL RESPONSE")).build());
572+
573+
var mcpServer = McpServer.sync(mcpServerTransportProvider)
574+
.capabilities(ServerCapabilities.builder()
575+
.tools(ServerCapabilities.ToolCapabilities.builder().search(true).build())
576+
.build())
577+
.tools(tool1)
578+
.toolSearchHandler((exchange, request) -> searchResponse)
579+
.build();
580+
581+
try (var mcpClient = clientBuilder.build()) {
582+
583+
InitializeResult initResult = mcpClient.initialize();
584+
assertThat(initResult).isNotNull();
585+
586+
assertThat(mcpClient.searchTools(SearchToolsRequest.builder().query("test").build()).tools())
587+
.contains(tool1.tool());
588+
589+
mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
590+
}
591+
592+
mcpServer.close();
593+
}
594+
562595
@ParameterizedTest(name = "{0} : {displayName} ")
563596
@ValueSource(strings = { "httpclient", "webflux" })
564597
void testToolListChangeHandlingSuccess(String clientType) {
@@ -631,6 +664,119 @@ void testToolListChangeHandlingSuccess(String clientType) {
631664
mcpServer.close();
632665
}
633666

667+
@ParameterizedTest(name = "{0} : {displayName} ")
668+
@ValueSource(strings = { "httpclient", "webflux" })
669+
void testSearchResourcesSuccess(String clientType) {
670+
671+
var clientBuilder = clientBuilders.get(clientType);
672+
673+
var tool1Spec = new McpSchema.Tool("tool1", "tool1 description", emptyJsonSchema);
674+
675+
var resource = new Resource("uri://test", "test", "test", "text/plain", null);
676+
var searchResponse = McpSchema.SearchResourcesResult.builder().resources(List.of(resource)).build();
677+
678+
McpServerFeatures.SyncToolSpecification tool1 = new McpServerFeatures.SyncToolSpecification(tool1Spec,
679+
(exchange, request) -> CallToolResult.builder().textContent(List.of("CALL RESPONSE")).build());
680+
681+
var mcpServer = McpServer.sync(mcpServerTransportProvider)
682+
.capabilities(ServerCapabilities.builder()
683+
.tools(ServerCapabilities.ToolCapabilities.builder().build())
684+
.resources(ServerCapabilities.ResourceCapabilities.builder().search(true).build())
685+
.build())
686+
.tools(tool1)
687+
.resourceSearchHandler((exchange, request) -> searchResponse)
688+
.build();
689+
690+
try (var mcpClient = clientBuilder.build()) {
691+
692+
InitializeResult initResult = mcpClient.initialize();
693+
assertThat(initResult).isNotNull();
694+
695+
assertThat(mcpClient.searchResources(SearchResourcesRequest.builder().query("test").build()).resources())
696+
.contains(resource);
697+
698+
mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
699+
}
700+
701+
mcpServer.close();
702+
}
703+
704+
@ParameterizedTest(name = "{0} : {displayName} ")
705+
@ValueSource(strings = { "httpclient", "webflux" })
706+
void testSearchResourceTemplatesSuccess(String clientType) {
707+
708+
var clientBuilder = clientBuilders.get(clientType);
709+
710+
var tool1Spec = new McpSchema.Tool("tool1", "tool1 description", emptyJsonSchema);
711+
712+
var resourceTemplate = new ResourceTemplate("uri://test", "test", "test", "text/plain", null);
713+
var searchResponse = McpSchema.SearchResourceTemplatesResult.builder()
714+
.resourceTemplates(List.of(resourceTemplate))
715+
.build();
716+
717+
McpServerFeatures.SyncToolSpecification tool1 = new McpServerFeatures.SyncToolSpecification(tool1Spec,
718+
(exchange, request) -> CallToolResult.builder().textContent(List.of("CALL RESPONSE")).build());
719+
720+
var mcpServer = McpServer.sync(mcpServerTransportProvider)
721+
.capabilities(ServerCapabilities.builder()
722+
.tools(ServerCapabilities.ToolCapabilities.builder().build())
723+
.resources(ServerCapabilities.ResourceCapabilities.builder().search(true).build())
724+
.build())
725+
.tools(tool1)
726+
.resourceTemplateSearchHandler((exchange, request) -> searchResponse)
727+
.build();
728+
729+
try (var mcpClient = clientBuilder.build()) {
730+
731+
InitializeResult initResult = mcpClient.initialize();
732+
assertThat(initResult).isNotNull();
733+
734+
assertThat(mcpClient.searchResourceTemplates(SearchResourceTemplatesRequest.builder().query("test").build())
735+
.resourceTemplates()).contains(resourceTemplate);
736+
737+
mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
738+
}
739+
740+
mcpServer.close();
741+
}
742+
743+
@ParameterizedTest(name = "{0} : {displayName} ")
744+
@ValueSource(strings = { "httpclient", "webflux" })
745+
void testSearchPromptsSuccess(String clientType) {
746+
747+
var clientBuilder = clientBuilders.get(clientType);
748+
749+
var tool1Spec = new McpSchema.Tool("tool1", "tool1 description", emptyJsonSchema);
750+
751+
var prompt = new Prompt("test", "test", List.of());
752+
var searchResponse = McpSchema.SearchPromptsResult.builder().prompts(List.of(prompt)).build();
753+
754+
McpServerFeatures.SyncToolSpecification tool1 = new McpServerFeatures.SyncToolSpecification(tool1Spec,
755+
(exchange, request) -> CallToolResult.builder().textContent(List.of("CALL RESPONSE")).build());
756+
757+
var mcpServer = McpServer.sync(mcpServerTransportProvider)
758+
.capabilities(ServerCapabilities.builder()
759+
.tools(ServerCapabilities.ToolCapabilities.builder().build())
760+
.prompts(ServerCapabilities.PromptCapabilities.builder().search(true).build())
761+
.build())
762+
.tools(tool1)
763+
.promptSearchHandler((exchange, request) -> searchResponse)
764+
.build();
765+
766+
try (var mcpClient = clientBuilder.build()) {
767+
768+
InitializeResult initResult = mcpClient.initialize();
769+
assertThat(initResult).isNotNull();
770+
771+
assertThat(mcpClient.searchPrompts(SearchPromptsRequest.builder().query("test").build()).prompts())
772+
.contains(prompt);
773+
774+
mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
775+
}
776+
777+
mcpServer.close();
778+
}
779+
634780
@ParameterizedTest(name = "{0} : {displayName} ")
635781
@ValueSource(strings = { "httpclient", "webflux" })
636782
void testInitialize(String clientType) {

mcp-spring/mcp-spring-webmvc/src/test/java/io/modelcontextprotocol/server/WebMvcSseIntegrationTests.java

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,145 @@ void testToolListChangeHandlingSuccess() {
635635
mcpServer.close();
636636
}
637637

638+
// ---------------------------------------
639+
// Search Tests
640+
// ---------------------------------------
641+
@Test
642+
void testSearchToolsSuccess() {
643+
644+
var tool1Spec = new McpSchema.Tool("tool1", "tool1 description", emptyJsonSchema);
645+
var searchResponse = McpSchema.SearchToolsResult.builder().tools(List.of(tool1Spec)).build();
646+
McpServerFeatures.SyncToolSpecification tool1 = new McpServerFeatures.SyncToolSpecification(tool1Spec,
647+
(exchange, request) -> CallToolResult.builder().textContent(List.of("CALL RESPONSE")).build());
648+
649+
var mcpServer = McpServer.sync(mcpServerTransportProvider)
650+
.capabilities(ServerCapabilities.builder()
651+
.tools(ServerCapabilities.ToolCapabilities.builder().search(true).build())
652+
.build())
653+
.tools(tool1)
654+
.toolSearchHandler((exchange, request) -> searchResponse)
655+
.build();
656+
657+
try (var mcpClient = clientBuilder.build()) {
658+
659+
InitializeResult initResult = mcpClient.initialize();
660+
assertThat(initResult).isNotNull();
661+
662+
assertThat(mcpClient.searchTools(McpSchema.SearchToolsRequest.builder().query("test").build()).tools())
663+
.contains(tool1.tool());
664+
665+
mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
666+
}
667+
668+
mcpServer.close();
669+
}
670+
671+
@Test
672+
void testSearchResourcesSuccess() {
673+
674+
var tool1Spec = new McpSchema.Tool("tool1", "tool1 description", emptyJsonSchema);
675+
676+
var resource = new McpSchema.Resource("uri://test", "test", "test", "text/plain", null);
677+
var searchResponse = McpSchema.SearchResourcesResult.builder().resources(List.of(resource)).build();
678+
679+
McpServerFeatures.SyncToolSpecification tool1 = new McpServerFeatures.SyncToolSpecification(tool1Spec,
680+
(exchange, request) -> CallToolResult.builder().textContent(List.of("CALL RESPONSE")).build());
681+
682+
var mcpServer = McpServer.sync(mcpServerTransportProvider)
683+
.capabilities(ServerCapabilities.builder()
684+
.tools(ServerCapabilities.ToolCapabilities.builder().build())
685+
.resources(ServerCapabilities.ResourceCapabilities.builder().search(true).build())
686+
.build())
687+
.tools(tool1)
688+
.resourceSearchHandler((exchange, request) -> searchResponse)
689+
.build();
690+
691+
try (var mcpClient = clientBuilder.build()) {
692+
693+
InitializeResult initResult = mcpClient.initialize();
694+
assertThat(initResult).isNotNull();
695+
696+
assertThat(mcpClient.searchResources(McpSchema.SearchResourcesRequest.builder().query("test").build())
697+
.resources()).contains(resource);
698+
699+
mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
700+
}
701+
702+
mcpServer.close();
703+
}
704+
705+
@Test
706+
void testSearchResourceTemplatesSuccess() {
707+
708+
var tool1Spec = new McpSchema.Tool("tool1", "tool1 description", emptyJsonSchema);
709+
710+
var resourceTemplate = new McpSchema.ResourceTemplate("uri://test", "test", "test", "text/plain", null);
711+
var searchResponse = McpSchema.SearchResourceTemplatesResult.builder()
712+
.resourceTemplates(List.of(resourceTemplate))
713+
.build();
714+
715+
McpServerFeatures.SyncToolSpecification tool1 = new McpServerFeatures.SyncToolSpecification(tool1Spec,
716+
(exchange, request) -> CallToolResult.builder().textContent(List.of("CALL RESPONSE")).build());
717+
718+
var mcpServer = McpServer.sync(mcpServerTransportProvider)
719+
.capabilities(ServerCapabilities.builder()
720+
.tools(ServerCapabilities.ToolCapabilities.builder().build())
721+
.resources(ServerCapabilities.ResourceCapabilities.builder().search(true).build())
722+
.build())
723+
.tools(tool1)
724+
.resourceTemplateSearchHandler((exchange, request) -> searchResponse)
725+
.build();
726+
727+
try (var mcpClient = clientBuilder.build()) {
728+
729+
InitializeResult initResult = mcpClient.initialize();
730+
assertThat(initResult).isNotNull();
731+
732+
assertThat(mcpClient
733+
.searchResourceTemplates(McpSchema.SearchResourceTemplatesRequest.builder().query("test").build())
734+
.resourceTemplates()).contains(resourceTemplate);
735+
736+
mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
737+
}
738+
739+
mcpServer.close();
740+
}
741+
742+
@Test
743+
void testSearchPromptsSuccess() {
744+
745+
var tool1Spec = new McpSchema.Tool("tool1", "tool1 description", emptyJsonSchema);
746+
747+
var prompt = new McpSchema.Prompt("test", "test", List.of());
748+
var searchResponse = McpSchema.SearchPromptsResult.builder().prompts(List.of(prompt)).build();
749+
750+
McpServerFeatures.SyncToolSpecification tool1 = new McpServerFeatures.SyncToolSpecification(tool1Spec,
751+
(exchange, request) -> CallToolResult.builder().textContent(List.of("CALL RESPONSE")).build());
752+
753+
var mcpServer = McpServer.sync(mcpServerTransportProvider)
754+
.capabilities(ServerCapabilities.builder()
755+
.tools(ServerCapabilities.ToolCapabilities.builder().build())
756+
.prompts(ServerCapabilities.PromptCapabilities.builder().search(true).build())
757+
.build())
758+
.tools(tool1)
759+
.promptSearchHandler((exchange, request) -> searchResponse)
760+
.build();
761+
762+
try (var mcpClient = clientBuilder.build()) {
763+
764+
InitializeResult initResult = mcpClient.initialize();
765+
assertThat(initResult).isNotNull();
766+
767+
assertThat(
768+
mcpClient.searchPrompts(McpSchema.SearchPromptsRequest.builder().query("test").build()).prompts())
769+
.contains(prompt);
770+
771+
mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
772+
}
773+
774+
mcpServer.close();
775+
}
776+
638777
@Test
639778
void testInitialize() {
640779

mcp/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,4 +838,63 @@ public Mono<McpSchema.CompleteResult> completeCompletion(McpSchema.CompleteReque
838838
.sendRequest(McpSchema.METHOD_COMPLETION_COMPLETE, completeRequest, COMPLETION_COMPLETE_RESULT_TYPE_REF));
839839
}
840840

841+
// --------------------------
842+
// Search
843+
// --------------------------
844+
845+
private static final TypeReference<McpSchema.SearchToolsResult> SEARCH_TOOLS_RESULT_TYPE_REF = new TypeReference<>() {
846+
};
847+
848+
private static final TypeReference<McpSchema.SearchResourcesResult> SEARCH_RESOURCES_RESULT_TYPE_REF = new TypeReference<>() {
849+
};
850+
851+
private static final TypeReference<McpSchema.SearchResourceTemplatesResult> SEARCH_RESOURCE_TEMPLATES_RESULT_TYPE_REF = new TypeReference<>() {
852+
};
853+
854+
private static final TypeReference<McpSchema.SearchPromptsResult> SEARCH_PROMPTS_RESULT_TYPE_REF = new TypeReference<>() {
855+
};
856+
857+
public Mono<McpSchema.SearchToolsResult> searchTools(McpSchema.SearchToolsRequest searchToolsRequest) {
858+
return this.withInitializationCheck("searching tools", initializedResult -> {
859+
if (this.serverCapabilities.tools() == null || !this.serverCapabilities.tools().search()) {
860+
return Mono.error(new McpError("Server does not provide the tool search capability"));
861+
}
862+
return this.mcpSession.sendRequest(McpSchema.METHOD_TOOLS_SEARCH, searchToolsRequest,
863+
SEARCH_TOOLS_RESULT_TYPE_REF);
864+
});
865+
}
866+
867+
public Mono<McpSchema.SearchResourcesResult> searchResources(
868+
McpSchema.SearchResourcesRequest searchResourcesRequest) {
869+
return this.withInitializationCheck("searching resources", initializedResult -> {
870+
if (this.serverCapabilities.resources() == null || !this.serverCapabilities.resources().search()) {
871+
return Mono.error(new McpError("Server does not provide the resource search capability"));
872+
}
873+
return this.mcpSession.sendRequest(McpSchema.METHOD_RESOURCES_SEARCH, searchResourcesRequest,
874+
SEARCH_RESOURCES_RESULT_TYPE_REF);
875+
});
876+
}
877+
878+
public Mono<McpSchema.SearchResourceTemplatesResult> searchResourceTemplates(
879+
McpSchema.SearchResourceTemplatesRequest searchResourceTemplatesRequest) {
880+
return this.withInitializationCheck("searching resource templates", initializedResult -> {
881+
if (this.serverCapabilities.resources() == null || !this.serverCapabilities.resources().search()) {
882+
// shares a capability with resources
883+
return Mono.error(new McpError("Server does not provide the resource search capability"));
884+
}
885+
return this.mcpSession.sendRequest(McpSchema.METHOD_RESOURCES_TEMPLATES_SEARCH,
886+
searchResourceTemplatesRequest, SEARCH_RESOURCE_TEMPLATES_RESULT_TYPE_REF);
887+
});
888+
}
889+
890+
public Mono<McpSchema.SearchPromptsResult> searchPrompts(McpSchema.SearchPromptsRequest searchPromptsRequest) {
891+
return this.withInitializationCheck("searching prompts", initializedResult -> {
892+
if (this.serverCapabilities.prompts() == null || !this.serverCapabilities.prompts().search()) {
893+
return Mono.error(new McpError("Server does not provide the tool search capability"));
894+
}
895+
return this.mcpSession.sendRequest(McpSchema.METHOD_PROMPT_SEARCH, searchPromptsRequest,
896+
SEARCH_PROMPTS_RESULT_TYPE_REF);
897+
});
898+
}
899+
841900
}

0 commit comments

Comments
 (0)