@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio
489 lines • 15.8 kB
JavaScript
/**
* MCP Registry Client
*
* Client for discovering MCP servers from centralized registries.
* Supports multiple registry sources including:
* - Official MCP Registry
* - NPM packages
* - GitHub repositories
* - Custom registries
*
* @module mcp/mcpRegistryClient
* @since 8.39.0
*/
import { EventEmitter } from "events";
/**
* Well-known MCP servers catalog
*/
const WELL_KNOWN_SERVERS = [
{
id: "filesystem",
name: "Filesystem",
description: "File system operations - read, write, create, list directories",
version: "1.0.0",
npmPackage: "@modelcontextprotocol/server-filesystem",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-filesystem"],
transports: ["stdio"],
categories: ["file-system"],
tags: ["files", "directories", "read", "write"],
tools: ["read_file", "write_file", "list_directory", "create_directory"],
verified: true,
},
{
id: "github",
name: "GitHub",
description: "GitHub repository management and file operations",
version: "1.0.0",
npmPackage: "@modelcontextprotocol/server-github",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-github"],
requiredEnvVars: ["GITHUB_PERSONAL_ACCESS_TOKEN"],
transports: ["stdio"],
categories: ["version-control", "api"],
tags: ["github", "git", "repositories", "issues", "pull-requests"],
tools: [
"create_repository",
"list_commits",
"create_issue",
"create_pull_request",
],
verified: true,
},
{
id: "postgres",
name: "PostgreSQL",
description: "PostgreSQL database query and management",
version: "1.0.0",
npmPackage: "@modelcontextprotocol/server-postgres",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-postgres"],
requiredEnvVars: ["DATABASE_URL"],
transports: ["stdio"],
categories: ["database"],
tags: ["postgres", "postgresql", "sql", "database", "query"],
tools: ["query", "list_tables", "describe_table"],
verified: true,
},
{
id: "sqlite",
name: "SQLite",
description: "SQLite database operations and queries",
version: "1.0.0",
npmPackage: "@modelcontextprotocol/server-sqlite",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-sqlite"],
transports: ["stdio"],
categories: ["database"],
tags: ["sqlite", "sql", "database", "local"],
tools: ["query", "list_tables", "describe_table"],
verified: true,
},
{
id: "brave-search",
name: "Brave Search",
description: "Web search using Brave Search API",
version: "1.0.0",
npmPackage: "@modelcontextprotocol/server-brave-search",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-brave-search"],
requiredEnvVars: ["BRAVE_API_KEY"],
transports: ["stdio"],
categories: ["search", "api"],
tags: ["search", "web", "brave", "internet"],
tools: ["web_search", "local_search"],
verified: true,
},
{
id: "puppeteer",
name: "Puppeteer",
description: "Web scraping and browser automation",
version: "1.0.0",
npmPackage: "@modelcontextprotocol/server-puppeteer",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-puppeteer"],
transports: ["stdio"],
categories: ["automation", "web"],
tags: ["browser", "scraping", "automation", "puppeteer"],
tools: ["navigate", "screenshot", "click", "type", "get_content"],
verified: true,
},
{
id: "git",
name: "Git",
description: "Git repository operations and version control",
version: "1.0.0",
npmPackage: "@modelcontextprotocol/server-git",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-git"],
transports: ["stdio"],
categories: ["version-control"],
tags: ["git", "vcs", "commits", "branches"],
tools: ["git_status", "git_log", "git_diff", "git_commit"],
verified: true,
},
{
id: "memory",
name: "Memory",
description: "Persistent memory and knowledge storage",
version: "1.0.0",
npmPackage: "@modelcontextprotocol/server-memory",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-memory"],
transports: ["stdio"],
categories: ["memory", "storage"],
tags: ["memory", "knowledge", "storage", "persistent"],
tools: ["store", "retrieve", "search", "delete"],
verified: true,
},
{
id: "slack",
name: "Slack",
description: "Slack workspace integration",
version: "1.0.0",
npmPackage: "@modelcontextprotocol/server-slack",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-slack"],
requiredEnvVars: ["SLACK_BOT_TOKEN"],
transports: ["stdio"],
categories: ["communication", "api"],
tags: ["slack", "messaging", "chat", "team"],
tools: ["send_message", "list_channels", "get_channel_history"],
verified: true,
},
{
id: "google-drive",
name: "Google Drive",
description: "Google Drive file management",
version: "1.0.0",
npmPackage: "@modelcontextprotocol/server-gdrive",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-gdrive"],
transports: ["stdio"],
categories: ["file-system", "api"],
tags: ["google", "drive", "files", "cloud"],
tools: ["list_files", "read_file", "create_file", "search_files"],
verified: true,
},
];
/**
* MCP Registry Client
*
* Provides methods to discover and install MCP servers from registries.
*
* @example
* ```typescript
* const client = new MCPRegistryClient();
*
* // Search for servers
* const results = await client.search({ query: "database" });
*
* // Get server details
* const entry = await client.getEntry("postgres");
*
* // Convert to MCPServerInfo
* const serverInfo = client.toServerInfo(entry);
* ```
*/
export class MCPRegistryClient extends EventEmitter {
config;
cache = new Map();
customEntries = new Map();
constructor(config = {}) {
super();
this.config = {
registries: config.registries ?? [
{ type: "official", enableCache: true },
],
enableCache: config.enableCache ?? true,
defaultCacheTTL: config.defaultCacheTTL ?? 3600000, // 1 hour
timeout: config.timeout ?? 10000,
userAgent: config.userAgent ?? "NeuroLink-MCP-Registry-Client/1.0",
};
}
/**
* Search the registry
*/
async search(options = {}) {
const { query, categories, tags, transport, verifiedOnly = false, sortBy = "downloads", sortDirection = "desc", limit: rawLimit = 25, offset: rawOffset = 0, } = options;
const limit = Math.max(1, rawLimit);
const offset = Math.max(0, rawOffset);
// Get all entries (from cache or fetch)
let entries = await this.getAllEntries();
// Apply filters
if (query) {
const searchLower = query.toLowerCase();
entries = entries.filter((e) => e.name.toLowerCase().includes(searchLower) ||
e.description.toLowerCase().includes(searchLower) ||
e.tags?.some((t) => t.toLowerCase().includes(searchLower)));
}
if (categories?.length) {
entries = entries.filter((e) => e.categories?.some((c) => categories.includes(c)));
}
if (tags?.length) {
entries = entries.filter((e) => e.tags?.some((t) => tags.includes(t)));
}
if (transport) {
entries = entries.filter((e) => e.transports?.includes(transport));
}
if (verifiedOnly) {
entries = entries.filter((e) => e.verified);
}
// Sort
entries.sort((a, b) => {
let comparison = 0;
switch (sortBy) {
case "name":
comparison = a.name.localeCompare(b.name);
break;
case "downloads":
comparison = (a.downloads ?? 0) - (b.downloads ?? 0);
break;
case "stars":
comparison = (a.stars ?? 0) - (b.stars ?? 0);
break;
case "lastUpdated":
comparison = (a.lastUpdated ?? "").localeCompare(b.lastUpdated ?? "");
break;
}
return sortDirection === "desc" ? -comparison : comparison;
});
const totalCount = entries.length;
// Apply pagination
entries = entries.slice(offset, offset + limit);
return {
entries,
totalCount,
page: Math.floor(offset / limit) + 1,
pageSize: limit,
hasMore: offset + entries.length < totalCount,
};
}
/**
* Get a specific entry by ID
*/
async getEntry(id) {
// Check custom entries first
if (this.customEntries.has(id)) {
return this.customEntries.get(id);
}
// Check well-known servers
const wellKnown = WELL_KNOWN_SERVERS.find((s) => s.id === id);
if (wellKnown) {
return wellKnown;
}
// TODO: Fetch from remote registry
return undefined;
}
/**
* Get all available entries
*/
async getAllEntries() {
const cacheKey = "all-entries";
// Check cache
if (this.config.enableCache) {
const cached = this.cache.get(cacheKey);
if (cached &&
Date.now() - cached.timestamp < this.config.defaultCacheTTL) {
return cached.data;
}
}
// Combine well-known servers and custom entries, deduplicating by ID.
// Custom entries take precedence over well-known servers.
const customIds = new Set(this.customEntries.keys());
const allEntries = [
...WELL_KNOWN_SERVERS.filter((entry) => !customIds.has(entry.id)),
...Array.from(this.customEntries.values()),
];
// Cache the result
if (this.config.enableCache) {
this.cache.set(cacheKey, { data: allEntries, timestamp: Date.now() });
}
return allEntries;
}
/**
* Get entries by category
*/
async getByCategory(category) {
const entries = await this.getAllEntries();
return entries.filter((e) => e.categories?.includes(category));
}
/**
* Get entries by tag
*/
async getByTag(tag) {
const entries = await this.getAllEntries();
return entries.filter((e) => e.tags?.includes(tag));
}
/**
* Get all categories
*/
async getCategories() {
const entries = await this.getAllEntries();
const categories = new Set();
for (const entry of entries) {
for (const category of entry.categories ?? []) {
categories.add(category);
}
}
return Array.from(categories).sort();
}
/**
* Get all tags
*/
async getTags() {
const entries = await this.getAllEntries();
const tags = new Set();
for (const entry of entries) {
for (const tag of entry.tags ?? []) {
tags.add(tag);
}
}
return Array.from(tags).sort();
}
/**
* Convert registry entry to MCPServerInfo
*/
toServerInfo(entry) {
return {
id: entry.id,
name: entry.name,
description: entry.description,
command: entry.command,
args: entry.args,
transport: entry.transports?.[0] ?? "stdio",
status: "stopped",
tools: entry.tools?.map((name) => ({
name,
description: `Tool: ${name}`,
})) ?? [],
metadata: {
...entry.metadata,
version: entry.version,
author: entry.author,
license: entry.license,
homepage: entry.homepage,
repository: entry.repository,
npmPackage: entry.npmPackage,
requiredEnvVars: entry.requiredEnvVars,
categories: entry.categories,
tags: entry.tags,
verified: entry.verified,
},
};
}
/**
* Add a custom registry entry
*/
addCustomEntry(entry) {
this.customEntries.set(entry.id, entry);
this.clearCache();
this.emit("entryAdded", { entry });
}
/**
* Remove a custom registry entry
*/
removeCustomEntry(id) {
const removed = this.customEntries.delete(id);
if (removed) {
this.clearCache();
this.emit("entryRemoved", { id });
}
return removed;
}
/**
* Add a registry configuration
*/
addRegistry(config) {
this.config.registries.push(config);
this.clearCache();
const { authToken: _, ...safeConfig } = config;
this.emit("registryAdded", { config: safeConfig });
}
/**
* Clear the cache
*/
clearCache() {
this.cache.clear();
this.emit("cacheCleared");
}
/**
* Check if required environment variables are set
*/
checkRequiredEnvVars(entry) {
const missing = [];
for (const envVar of entry.requiredEnvVars ?? []) {
if (!process.env[envVar]) {
missing.push(envVar);
}
}
return {
ready: missing.length === 0,
missing,
};
}
/**
* Get installation command for an entry
*/
getInstallCommand(entry) {
if (entry.installCommand) {
return entry.installCommand;
}
if (entry.npmPackage) {
return `npx -y ${entry.npmPackage}`;
}
return undefined;
}
/**
* Get popular servers
*/
async getPopularServers(limit = 10) {
const result = await this.search({
sortBy: "downloads",
sortDirection: "desc",
limit,
});
return result.entries;
}
/**
* Get verified servers
*/
async getVerifiedServers() {
const result = await this.search({
verifiedOnly: true,
});
return result.entries;
}
/**
* Get statistics
*/
async getStatistics() {
const entries = await this.getAllEntries();
const categories = await this.getCategories();
const tags = await this.getTags();
return {
totalEntries: entries.length,
verifiedEntries: entries.filter((e) => e.verified).length,
categories: categories.length,
tags: tags.length,
customEntries: this.customEntries.size,
};
}
}
/**
* Global MCP registry client instance
*/
export const globalMCPRegistryClient = new MCPRegistryClient();
/**
* Quick lookup function for well-known servers
*/
export function getWellKnownServer(id) {
return WELL_KNOWN_SERVERS.find((s) => s.id === id);
}
/**
* Get all well-known servers
*/
export function getAllWellKnownServers() {
return [...WELL_KNOWN_SERVERS];
}
//# sourceMappingURL=mcpRegistryClient.js.map