MCP Registry
The MCP module provides a unified registry that aggregates MCP server entries from 4 public sources, with deduplication, caching, and protocol version filtering. It also includes a testing toolkit for MCP server development.
UnifiedMcpRegistry
The UnifiedMcpRegistry is a one-stop shop for discovering MCP servers. It queries multiple public registries (like the official MCP registry and Smithery.ai), deduplicates results by server name, and caches responses so repeated searches are fast.
// Default: all 4 sources, 10-minute cache
UnifiedMcpRegistry registry = new UnifiedMcpRegistry();
// Search for servers
List<McpServerEntry> results = registry.search("filesystem");
// Look up a specific server
Optional<McpServerEntry> server = registry.getServer("filesystem");
// List all available servers (aggregated from all sources)
List<McpServerEntry> all = registry.listAll();Custom Configuration
You can pick which registry sources to use and set a custom cache TTL. This is useful if you only want results from specific sources or need fresher data.
UnifiedMcpRegistry registry = new UnifiedMcpRegistry(
List.of(
new OfficialRegistryClient(),
new SmitheryClient()
),
Duration.ofMinutes(5) // Custom cache TTL
);Source Priority
When the same server appears in multiple registries, the registry uses priority to decide which entry to keep. Higher-priority sources are considered more authoritative.
| Priority | Source | Class | Registry |
|---|---|---|---|
| 4 (highest) | official | OfficialRegistryClient | Official MCP Registry |
| 3 | camel | CamelMcpHubClient | Apache Camel MCP Hub |
| 2 | smithery | SmitheryClient | Smithery.ai |
| 1 (lowest) | mcpservers.org | McpServersOrgClient | mcpservers.org |
Caching
To avoid hitting remote registries on every search, results are cached per query key with a configurable TTL (default: 10 minutes). The cache uses ConcurrentHashMap for thread safety. Cache entries include the timestamp of insertion and are evicted on the next access after TTL expiry.
Resilience
The registry is designed to be fault-tolerant. If a source fails (network error, parsing error), the registry logs a warning and continues with results from the remaining sources. A partial result set is always better than a failed query.
McpRegistrySource Interface
If you run your own internal MCP server catalog, you can add it as a custom registry source. Implement this interface and pass it to the UnifiedMcpRegistry constructor.
public interface McpRegistrySource {
String getSourceName();
List<McpServerEntry> search(String query) throws IOException;
Optional<McpServerEntry> getServer(String name) throws IOException;
List<McpServerEntry> listAll() throws IOException;
}Built-in Sources
These four sources are included out of the box and are all enabled by default.
| Source | Description |
|---|---|
OfficialRegistryClient | Queries the official MCP server registry |
CamelMcpHubClient | Queries the Apache Camel MCP Hub |
SmitheryClient | Queries the Smithery.ai marketplace |
McpServersOrgClient | Queries the mcpservers.org community directory |
McpServerEntry
Each discovered MCP server is represented as an McpServerEntry record. It contains everything you need to connect to the server, including its transport type, endpoint, and protocol version.
public record McpServerEntry(
String name, // Unique server name (required)
String description, // Human-readable description
String version, // Server version string
String protocolVersion, // MCP protocol version (e.g. "2025-11-05")
String transport, // "stdio", "sse", or "streamable-http"
String endpoint, // Connection URL or command
String source, // Registry source name
double rating, // Quality rating (0.0 - 5.0)
Map<String, String> metadata // Additional key-value pairs
) {}Protocol Version Filtering
Not all servers support the same MCP protocol version. Use protocol version filtering to ensure you only connect to servers compatible with your client's version.
List<McpServerEntry> servers = registry.search("database");
// Filter to servers supporting protocol version 2025-03-26 or later
List<McpServerEntry> compatible = servers.stream()
.filter(s -> McpProtocolVersion.isCompatible(s.protocolVersion(), "2025-03-26"))
.toList();Testing Toolkit
The MCP module includes JUnit 5 utilities for testing MCP server implementations.
McpServerTestKit
The McpServerTestKit is a JUnit 5 test harness that validates whether your MCP server implementation is compliant with the protocol. It automatically tests initialization, tool listing, tool invocation, and error handling.
class MyMcpServerTest {
@Test
void testServerCompliance() {
McpServerTestKit testKit = McpServerTestKit.builder()
.transport(new StdioTransport.Builder()
.command("node", "my-server.js")
.build())
.build();
// Validates initialize, tools/list, tool invocation, and error handling
testKit.runComplianceTests();
}
@Test
void testSpecificTool() {
McpServerTestKit testKit = McpServerTestKit.builder()
.transport(transport)
.build();
// Test a specific tool
var result = testKit.callTool("read_file", Map.of("path", "/tmp/test.txt"));
assertNotNull(result);
}
}MockMcpServer
When you want to test your MCP client code without a real server, use MockMcpServer. It runs entirely in memory with no network I/O, making tests fast and deterministic.
MockMcpServer mockServer = MockMcpServer.builder()
.tool("read_file", "Read a file", Map.of(
"type", "object",
"properties", Map.of("path", Map.of("type", "string"))
))
.toolHandler("read_file", args -> "file content here")
.build();
// Use with McpClient for testing
McpClient client = new McpClient(mockServer.getTransport());
client.connect();
var tools = client.listTools();
// tools contains "read_file"MockMcpServer uses MockMcpTransport internally, which implements McpTransport with in-memory message queues instead of network I/O.
McpSchemaValidator
The McpSchemaValidator checks that your JSON-RPC messages conform to the MCP specification. Use it during development to catch schema issues early, such as missing required fields or invalid types in tool definitions.
McpSchemaValidator validator = new McpSchemaValidator();
// Validate a tool definition
ValidationResult result = validator.validateToolDefinition(toolJson);
if (!result.isValid()) {
result.getErrors().forEach(System.err::println);
}
// Validate a JSON-RPC request
ValidationResult result = validator.validateRequest(requestJson);
// Validate a JSON-RPC response
ValidationResult result = validator.validateResponse(responseJson);The validator checks required fields, type constraints, and MCP-specific schema rules (e.g., tool input schemas must be valid JSON Schema).