blob: 6ceae3ba1ddeac6f61be5b65442a750c86fd496a [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.mcp.server;
import static org.junit.jupiter.api.Assertions.*;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.spec.McpClientTransport;
import io.modelcontextprotocol.spec.McpSchema.ListToolsResult;
import io.modelcontextprotocol.spec.McpSchema.Tool;
import java.util.List;
import java.util.Set;
/**
* Sample MCP client for testing and demonstrating Solr MCP Server functionality.
*
* <p>This test client provides a comprehensive validation suite for the Solr MCP Server, verifying
* that all expected MCP tools are properly registered and functioning as expected. It serves as
* both a testing framework and a reference implementation for MCP client integration.
*
* <p><strong>Test Coverage:</strong>
*
* <ul>
* <li><strong>Client Initialization</strong>: Verifies MCP client can connect and initialize
* <li><strong>Connection Health</strong>: Tests ping functionality and connection stability
* <li><strong>Tool Discovery</strong>: Validates all expected MCP tools are registered
* <li><strong>Tool Validation</strong>: Checks tool metadata, descriptions, and schemas
* <li><strong>Expected Tools</strong>: Verifies presence of search, indexing, and metadata tools
* </ul>
*
* <p><strong>Expected MCP Tools:</strong>
*
* <ul>
* <li><strong>index_json_documents</strong>: JSON document indexing capability
* <li><strong>index_csv_documents</strong>: CSV document indexing capability
* <li><strong>index_xml_documents</strong>: XML document indexing capability
* <li><strong>Search</strong>: Full-text search functionality with filtering and faceting
* <li><strong>listCollections</strong>: Collection discovery and listing
* <li><strong>getCollectionStats</strong>: Collection metrics and performance data
* <li><strong>checkHealth</strong>: Health monitoring and status reporting
* <li><strong>getSchema</strong>: Schema introspection and field analysis
* </ul>
*
* <p><strong>Usage Example:</strong>
*
* <pre>{@code
* McpClientTransport transport = // ... initialize transport
* SampleClient client = new SampleClient(transport);
* client.run(); // Executes full test suite
* }</pre>
*
* <p><strong>Assertion Strategy:</strong>
*
* <p>Uses JUnit assertions to validate expected behavior and fail fast on any inconsistencies. Each
* tool is validated for proper name, description, and schema configuration to ensure MCP protocol
* compliance.
*
* @version 0.0.1
* @since 0.0.1
* @see McpClient
* @see McpClientTransport
* @see io.modelcontextprotocol.spec.McpSchema.Tool
*/
public class SampleClient {
private final McpClientTransport transport;
/**
* Constructs a new SampleClient with the specified MCP transport.
*
* @param transport the MCP client transport for communication with the Solr MCP Server
* @throws IllegalArgumentException if transport is null
*/
public SampleClient(McpClientTransport transport) {
this.transport = transport;
}
/**
* Executes the comprehensive test suite for Solr MCP Server functionality.
*
* <p>This method performs a complete validation of the MCP server including:
*
* <ul>
* <li>Client initialization and connection establishment
* <li>Health check via ping operation
* <li>Tool discovery and count validation
* <li>Individual tool metadata validation
* <li>Tool-specific description and schema verification
* </ul>
*
* <p><strong>Test Sequence:</strong>
*
* <ol>
* <li>Initialize MCP client with provided transport
* <li>Perform ping test to verify connectivity
* <li>List all available tools and validate expected count (8 tools)
* <li>Verify each expected tool is present in the tools list
* <li>Validate tool metadata (name, description, schema) for each tool
* <li>Perform tool-specific validation based on tool type
* </ol>
*
* @throws RuntimeException if any test assertion fails or MCP operations encounter errors
* @throws AssertionError if expected tools are missing or tool validation fails
*/
public void run() {
try (var client =
McpClient.sync(this.transport)
.loggingConsumer(
message -> System.out.println(">> Client Logging: " + message))
.build()) {
// Assert client initialization succeeds
assertDoesNotThrow(
client::initialize, "Client initialization should not throw an exception");
// Assert ping succeeds
assertDoesNotThrow(client::ping, "Client ping should not throw an exception");
// List and validate tools
ListToolsResult toolsList = client.listTools();
assertNotNull(toolsList, "Tools list should not be null");
assertNotNull(toolsList.tools(), "Tools collection should not be null");
// Validate expected tool count based on MCP server implementation
assertEquals(8, toolsList.tools().size(), "Expected 8 tools to be available");
// Define expected tools based on the log output
Set<String> expectedToolNames =
Set.of(
"index_json_documents",
"index_csv_documents",
"getCollectionStats",
"Search",
"listCollections",
"checkHealth",
"index_xml_documents",
"getSchema");
// Validate each expected tool is present
List<String> actualToolNames = toolsList.tools().stream().map(Tool::name).toList();
for (String expectedTool : expectedToolNames) {
assertTrue(
actualToolNames.contains(expectedTool),
"Expected tool '" + expectedTool + "' should be available");
}
// Validate tool details for key tools
toolsList
.tools()
.forEach(
tool -> {
assertNotNull(tool.name(), "Tool name should not be null");
assertNotNull(
tool.description(), "Tool description should not be null");
assertNotNull(
tool.inputSchema(), "Tool input schema should not be null");
assertFalse(
tool.name().trim().isEmpty(),
"Tool name should not be empty");
assertFalse(
tool.description().trim().isEmpty(),
"Tool description should not be empty");
// Validate specific tools based on expected behavior
switch (tool.name()) {
case "index_json_documents":
assertTrue(
tool.description().toLowerCase().contains("json"),
"JSON indexing tool should mention JSON in"
+ " description");
break;
case "index_csv_documents":
assertTrue(
tool.description().toLowerCase().contains("csv"),
"CSV indexing tool should mention CSV in"
+ " description");
break;
case "Search":
assertTrue(
tool.description().toLowerCase().contains("search"),
"Search tool should mention search in description");
break;
case "listCollections":
assertTrue(
tool.description()
.toLowerCase()
.contains("collection"),
"List collections tool should mention collections"
+ " in description");
break;
case "checkHealth":
assertTrue(
tool.description().toLowerCase().contains("health"),
"Health check tool should mention health in"
+ " description");
break;
default:
// Additional tools are acceptable
break;
}
System.out.println(
"Tool: "
+ tool.name()
+ ", description: "
+ tool.description()
+ ", schema: "
+ tool.inputSchema());
});
} catch (Exception e) {
throw new RuntimeException("MCP client operation failed", e);
}
}
}