npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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-registry

Quick 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 | undefined

getServers()

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 | undefined

getTools()

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(): RegistryStats

Returns:

{
  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 received

Example 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 available

Configuration

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:coverage

Building

npm run build

Linting

npm run lint

Dependencies

  • @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