TnsAI
MCP

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.

PrioritySourceClassRegistry
4 (highest)officialOfficialRegistryClientOfficial MCP Registry
3camelCamelMcpHubClientApache Camel MCP Hub
2smitherySmitheryClientSmithery.ai
1 (lowest)mcpservers.orgMcpServersOrgClientmcpservers.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.

SourceDescription
OfficialRegistryClientQueries the official MCP server registry
CamelMcpHubClientQueries the Apache Camel MCP Hub
SmitheryClientQueries the Smithery.ai marketplace
McpServersOrgClientQueries 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).

On this page