We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/jmagar/homelab-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
/**
* Tool registry pattern for extensible tool registration
*
* Provides a plugin-style API for registering MCP tools without modifying
* the core registration code.
*
* @example
* ```typescript
* // Register built-in tools
* const registry = new ToolRegistry(container);
* registry.register(FluxTool);
* registry.register(ScoutTool);
*
* // Register custom tool
* registry.register(CustomTool);
*
* // Apply to MCP server
* registry.registerAll(server);
* ```
*/
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { getSchemaDescription } from "@modelcontextprotocol/sdk/server/zod-compat.js";
import type { z } from "zod";
import type { ServiceContainer } from "../services/container.js";
import { logError, sanitizeParams } from "../utils/errors.js";
/**
* Tool definition interface - implement this to create a new tool
*/
export interface ToolDefinition {
/** Unique tool name (must be unique across all registered tools) */
name: string;
/** Human-readable tool title */
title: string;
/** Tool description (if not provided, extracted from schema) */
description?: string;
/** Zod schema for input validation */
inputSchema: z.ZodTypeAny;
/** Optional Zod schema for MCP tool advertising (client-side input schema) */
mcpInputSchema?: z.ZodTypeAny;
/** Tool annotations for MCP SDK */
annotations?: {
readOnlyHint?: boolean;
destructiveHint?: boolean;
idempotentHint?: boolean;
openWorldHint?: boolean;
};
/**
* Tool handler function - implement your tool logic here
*
* @param params - Validated input parameters
* @param container - Service container for dependency injection
* @returns Tool output string (markdown or JSON)
*/
handler: (params: unknown, container: ServiceContainer) => Promise<string>;
}
/**
* Tool registry for managing tool registration
*
* Maintains collection of tools and provides registration API for MCP server.
* Supports plugin-style extension by allowing registration of custom tools.
*/
export class ToolRegistry {
private tools: Map<string, ToolDefinition> = new Map();
constructor(private container: ServiceContainer) {}
/**
* Register a tool definition
*
* @param tool - Tool definition to register
* @throws {Error} If tool name conflicts with existing tool
*
* @example
* ```typescript
* registry.register({
* name: "my-tool",
* title: "My Custom Tool",
* inputSchema: MySchema,
* handler: async (params, container) => {
* // Your tool implementation
* return "Tool output";
* }
* });
* ```
*/
register(tool: ToolDefinition): void {
if (this.tools.has(tool.name)) {
throw new Error(`Tool "${tool.name}" is already registered`);
}
this.tools.set(tool.name, tool);
}
/**
* Check if a tool is registered
*
* @param name - Tool name to check
* @returns True if tool is registered
*/
has(name: string): boolean {
return this.tools.has(name);
}
/**
* Get registered tool definition
*
* @param name - Tool name to get
* @returns Tool definition or undefined if not found
*/
get(name: string): ToolDefinition | undefined {
return this.tools.get(name);
}
/**
* Get all registered tool names
*
* @returns Array of tool names
*/
getToolNames(): string[] {
return Array.from(this.tools.keys());
}
/**
* Register all tools with MCP server
*
* Iterates through all registered tools and registers them with the MCP server
* using the SDK 1.25.1 API. Wraps handlers with error logging.
*
* @param server - MCP server instance
*
* @example
* ```typescript
* const registry = new ToolRegistry(container);
* registry.register(FluxTool);
* registry.register(ScoutTool);
* registry.registerAll(server);
* ```
*/
registerAll(server: McpServer): void {
for (const [name, tool] of this.tools.entries()) {
server.registerTool(
name,
{
title: tool.title,
description:
tool.description ?? getSchemaDescription(tool.inputSchema) ?? `${tool.title}`,
inputSchema: tool.mcpInputSchema ?? tool.inputSchema,
annotations: tool.annotations ?? {
readOnlyHint: false,
destructiveHint: false,
idempotentHint: false,
openWorldHint: true,
},
},
async (params: unknown) => {
try {
const result = await tool.handler(params, this.container);
return { content: [{ type: "text", text: result }] };
} catch (error) {
logError(error, {
operation: `${name}:handler`,
metadata: {
message: `${tool.title} execution failed`,
params: sanitizeParams(params),
container: { type: this.container.constructor.name },
},
});
throw error;
}
}
);
}
}
/**
* Unregister a tool
*
* @param name - Tool name to unregister
* @returns True if tool was removed, false if it didn't exist
*/
unregister(name: string): boolean {
return this.tools.delete(name);
}
/**
* Clear all registered tools
*
* Useful for testing or rebuilding registry from scratch.
*/
clear(): void {
this.tools.clear();
}
}