@bernierllc/mcp-registry
v1.0.6
Published
MCP tool registry with service discovery and capability-based querying
Downloads
76
Readme
@bernierllc/mcp-registry
MCP tool registry with service discovery and capability-based querying
Overview
@bernierllc/mcp-registry provides a centralized registry for MCP (Model Context Protocol) tools with powerful service discovery capabilities. Built on top of @bernierllc/mcp-server-core, it enables:
- Tool Registration: Register MCP tools with rich metadata
- Service Discovery: Find tools by capability, category, or tags
- Server Management: Track multiple MCP servers and their tools
- Capability Querying: Query tools by their capabilities (query, mutation, etc.)
- Optional NeverHub Integration: Publish events and integrate with NeverHub when available
Installation
npm install @bernierllc/mcp-registryQuick Start
import { MCPRegistry } from '@bernierllc/mcp-registry';
// Create registry
const registry = new MCPRegistry({
name: 'global-mcp-registry',
autoCleanup: true,
inactivityTimeout: 300000 // 5 minutes
});
// Register a server
await registry.registerServer({
id: 'planning-tools-001',
name: 'planning-tools',
version: '1.0.0',
description: 'Universal Planning System tools'
});
// Register a tool
await registry.registerTool({
id: 'planning-tools-001:planning.start_session',
name: 'planning.start_session',
description: 'Start a new planning session',
serverId: 'planning-tools-001',
serverName: 'planning-tools',
serverVersion: '1.0.0',
capabilities: [
{ type: 'mutation', name: 'planning', version: '1.0.0' }
],
category: 'planning',
tags: ['session', 'create']
});
// Query by capability
const result = await registry.queryByCapability({
type: 'mutation',
name: 'planning'
});
console.log(`Found ${result.data?.count} tools`);Core Concepts
Capabilities
Capabilities describe what a tool can do:
- type: The operation type (e.g., 'query', 'mutation', 'search')
- name: The domain (e.g., 'planning', 'packages', 'users')
- version: Semantic version of the capability
{
type: 'query',
name: 'packages',
version: '1.0.0',
description: 'Query package data'
}Tool Registration
Tools are registered with complete metadata:
await registry.registerTool({
id: 'server-id:tool-name',
name: 'packages.query',
description: 'Query packages with filters',
serverId: 'packages-server-001',
serverName: 'packages-api',
serverVersion: '1.0.0',
capabilities: [
{ type: 'query', name: 'packages', version: '1.0.0' }
],
category: 'packages',
tags: ['query', 'search', 'filter'],
version: '1.0.0'
});Service Discovery
Query tools using flexible filters:
// Query by capability type
const queryTools = await registry.queryByCapability({ type: 'query' });
// Query by capability name
const packageTools = await registry.queryByCapability({ name: 'packages' });
// Query by category
const planningTools = await registry.queryByCapability({ category: 'planning' });
// Query by tags
const createTools = await registry.queryByCapability({ tags: ['create'] });
// Combine filters
const result = await registry.queryByCapability({
type: 'mutation',
category: 'packages',
tags: ['create']
});API Reference
MCPRegistry
Constructor
new MCPRegistry(config?: RegistryConfig)Config Options:
name?: string- Registry name (default: 'mcp-registry')autoCleanup?: boolean- Enable automatic cleanup of inactive servers (default: true)inactivityTimeout?: number- Timeout in milliseconds (default: 300000 / 5 minutes)enableNeverHub?: boolean- Enable NeverHub integration (default: false)logger?: Logger- Optional logger instance
Server Management
registerServer()
await registry.registerServer({
id: string,
name: string,
version: string,
description?: string,
metadata?: Record<string, any>
}): Promise<RegistryResult<ServerRegistration>>unregisterServer()
await registry.unregisterServer(serverId: string): Promise<RegistryResult<void>>heartbeat()
await registry.heartbeat(serverId: string): Promise<RegistryResult<void>>getServer()
registry.getServer(serverId: string): ServerRegistration | undefinedgetServers()
registry.getServers(): ServerRegistration[]Tool Management
registerTool()
await registry.registerTool({
id: string,
name: string,
description: string,
serverId: string,
serverName: string,
serverVersion: string,
capabilities: Capability[],
category?: string,
tags?: string[],
version?: string,
metadata?: Record<string, any>
}): Promise<RegistryResult<ToolRegistration>>unregisterTool()
await registry.unregisterTool(toolId: string): Promise<RegistryResult<void>>getTool()
registry.getTool(toolId: string): ToolRegistration | undefinedgetTools()
registry.getTools(): ToolRegistration[]getToolsByServer()
registry.getToolsByServer(serverId: string): ToolRegistration[]Capability Querying
queryByCapability()
await registry.queryByCapability({
type?: string,
name?: string,
version?: string,
category?: string,
tags?: string[],
serverId?: string
}): Promise<RegistryResult<QueryResult>>Returns:
{
success: boolean,
data?: {
tools: ToolRegistration[],
count: number,
executionTime: number
},
error?: string
}Statistics
getStats()
registry.getStats(): RegistryStatsReturns:
{
serverCount: number,
toolCount: number,
activeServers: number,
capabilityCount: number,
toolsByCategory: Record<string, number>,
topTags: Array<{ tag: string, count: number }>
}Lifecycle
shutdown()
await registry.shutdown(): Promise<void>Stops auto-cleanup and disconnects from NeverHub if connected.
Types
Capability
interface Capability {
type: string;
name: string;
version: string;
description?: string;
metadata?: Record<string, any>;
}ToolRegistration
interface ToolRegistration {
id: string;
name: string;
description: string;
serverId: string;
serverName: string;
serverVersion: string;
capabilities: Capability[];
category?: string;
tags?: string[];
version?: string;
registeredAt: Date;
lastActiveAt: Date;
metadata?: Record<string, any>;
}ServerRegistration
interface ServerRegistration {
id: string;
name: string;
version: string;
description?: string;
toolCount: number;
status: 'active' | 'inactive' | 'error';
registeredAt: Date;
lastHeartbeat: Date;
metadata?: Record<string, any>;
}Usage Examples
Example 1: Multi-Server Registry
import { MCPRegistry } from '@bernierllc/mcp-registry';
const registry = new MCPRegistry({ name: 'production-registry' });
// Register planning server
await registry.registerServer({
id: 'planning-001',
name: 'planning-tools',
version: '1.0.0'
});
// Register packages server
await registry.registerServer({
id: 'packages-001',
name: 'packages-api',
version: '1.0.0'
});
// Register tools from both servers
await registry.registerTool({
id: 'planning-001:start_session',
name: 'planning.start_session',
description: 'Start planning session',
serverId: 'planning-001',
serverName: 'planning-tools',
serverVersion: '1.0.0',
capabilities: [{ type: 'mutation', name: 'planning', version: '1.0.0' }],
category: 'planning'
});
await registry.registerTool({
id: 'packages-001:query',
name: 'packages.query',
description: 'Query packages',
serverId: 'packages-001',
serverName: 'packages-api',
serverVersion: '1.0.0',
capabilities: [{ type: 'query', name: 'packages', version: '1.0.0' }],
category: 'packages'
});
// Find all query capabilities
const queryTools = await registry.queryByCapability({ type: 'query' });
console.log(`Found ${queryTools.data?.count} query tools`);Example 2: Server Heartbeat Management
import { MCPRegistry } from '@bernierllc/mcp-registry';
const registry = new MCPRegistry({
autoCleanup: true,
inactivityTimeout: 300000 // 5 minutes
});
// Register server
await registry.registerServer({
id: 'worker-001',
name: 'worker-service',
version: '1.0.0'
});
// Send heartbeats periodically
setInterval(async () => {
const result = await registry.heartbeat('worker-001');
if (result.success) {
console.log('Heartbeat sent successfully');
}
}, 60000); // Every minute
// Registry will automatically mark server as inactive if no heartbeat receivedExample 3: Complex Capability Queries
import { MCPRegistry } from '@bernierllc/mcp-registry';
const registry = new MCPRegistry();
// ... register servers and tools ...
// Find all mutation tools in the planning category
const result1 = await registry.queryByCapability({
type: 'mutation',
category: 'planning'
});
// Find tools with specific tags
const result2 = await registry.queryByCapability({
tags: ['create', 'session']
});
// Find tools for a specific server
const result3 = await registry.queryByCapability({
serverId: 'planning-001'
});
// Combine multiple filters
const result4 = await registry.queryByCapability({
type: 'query',
category: 'packages',
tags: ['search']
});Example 4: Registry Statistics
import { MCPRegistry } from '@bernierllc/mcp-registry';
const registry = new MCPRegistry();
// ... register servers and tools ...
// Get comprehensive statistics
const stats = registry.getStats();
console.log(`Total servers: ${stats.serverCount}`);
console.log(`Total tools: ${stats.toolCount}`);
console.log(`Active servers: ${stats.activeServers}`);
console.log(`Unique capabilities: ${stats.capabilityCount}`);
console.log('Tools by category:');
Object.entries(stats.toolsByCategory).forEach(([category, count]) => {
console.log(` ${category}: ${count}`);
});
console.log('Top tags:');
stats.topTags.forEach(({ tag, count }) => {
console.log(` ${tag}: ${count}`);
});NeverHub Integration
The registry supports optional NeverHub integration for event publishing and service coordination:
import { MCPRegistry } from '@bernierllc/mcp-registry';
// Enable NeverHub integration
const registry = new MCPRegistry({
enableNeverHub: true
});
// When NeverHub is available, events are automatically published:
// - server.registered
// - server.unregistered
// - tool.registered
// - tool.unregistered
// The registry gracefully degrades if NeverHub is not availableConfiguration
Environment Variables
The registry does not require environment variables, but you can configure it programmatically:
const registry = new MCPRegistry({
name: process.env.REGISTRY_NAME || 'default-registry',
autoCleanup: process.env.REGISTRY_AUTO_CLEANUP === 'true',
inactivityTimeout: parseInt(process.env.REGISTRY_TIMEOUT || '300000'),
enableNeverHub: process.env.ENABLE_NEVERHUB === 'true'
});Integration Status
- Logger: Optional - Compatible with
@bernierllc/logger, uses console as fallback - Docs-Suite: Ready - Complete API documentation with TypeDoc
- NeverHub: Optional - Publishes events when available, graceful degradation
Error Handling
All operations return a RegistryResult<T> with structured error handling:
interface RegistryResult<T> {
success: boolean;
data?: T;
error?: string;
}
// Example usage
const result = await registry.registerServer({...});
if (!result.success) {
console.error('Registration failed:', result.error);
} else {
console.log('Server registered:', result.data);
}Performance Considerations
- Query Performance: O(n) where n is the number of registered tools. Suitable for registries with thousands of tools.
- Memory: Each tool registration uses ~1KB of memory. A registry with 1000 tools uses ~1MB.
- Auto-Cleanup: Runs every 60 seconds. Minimal CPU impact.
Testing
# Run tests
npm test
# Run tests once
npm run test:run
# Run with coverage
npm run test:coverageBuilding
npm run buildLinting
npm run lintDependencies
@bernierllc/mcp-server-core- Core MCP server framework
Optional Dependencies
@bernierllc/neverhub-adapter- NeverHub integration (optional)
License
Copyright (c) 2025 Bernier LLC. All rights reserved.
This file is licensed to the client under a limited-use license. The client may use and modify this code only within the scope of the project it was delivered for. Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
See Also
- @bernierllc/mcp-server-core - Generic MCP server framework
- @bernierllc/neverhub-adapter - NeverHub integration adapter
