Skip to main content
Glama
registry.ts6.7 kB
/** * Dynamic tool registry for session-based tool management */ import { MCPTool, SessionInfo } from '../types/core.js'; import { EventEmitter } from 'events'; export interface ToolHandler { (params: any, context: { session_id?: string; request_id: string }): Promise<any>; } export interface RegisteredTool { tool: MCPTool; handler: ToolHandler; session_id?: string; registered_at: string; } export interface RegistryEvents { 'tool_registered': (toolName: string, sessionId?: string) => void; 'tool_unregistered': (toolName: string, sessionId?: string) => void; 'session_created': (sessionId: string, type: string) => void; 'session_destroyed': (sessionId: string, type: string) => void; } export class ToolRegistry extends EventEmitter { private tools = new Map<string, RegisteredTool>(); private sessions = new Map<string, SessionInfo>(); private sessionTools = new Map<string, Set<string>>(); // session_id -> tool names constructor() { super(); } /** * Register a static tool (available always) */ registerTool(name: string, tool: MCPTool, handler: ToolHandler): void { this.tools.set(name, { tool, handler, registered_at: new Date().toISOString() }); this.emit('tool_registered', name); } /** * Register a session-based tool (only available when session exists) */ registerSessionTool(sessionId: string, name: string, tool: MCPTool, handler: ToolHandler): void { const toolName = `${name}`; this.tools.set(toolName, { tool: { ...tool, name: toolName }, handler, session_id: sessionId, registered_at: new Date().toISOString() }); if (!this.sessionTools.has(sessionId)) { this.sessionTools.set(sessionId, new Set()); } this.sessionTools.get(sessionId)!.add(toolName); this.emit('tool_registered', toolName, sessionId); } /** * Unregister a specific tool */ unregisterTool(name: string): boolean { const tool = this.tools.get(name); if (!tool) { return false; } this.tools.delete(name); if (tool.session_id) { const sessionTools = this.sessionTools.get(tool.session_id); if (sessionTools) { sessionTools.delete(name); if (sessionTools.size === 0) { this.sessionTools.delete(tool.session_id); } } } this.emit('tool_unregistered', name, tool.session_id); return true; } /** * Create a session and mark it as active */ createSession(sessionId: string, type: 'browser' | 'db', metadata?: any): void { const sessionInfo: SessionInfo = { id: sessionId, type, created_at: new Date().toISOString(), last_used: new Date().toISOString(), metadata }; this.sessions.set(sessionId, sessionInfo); this.emit('session_created', sessionId, type); } /** * Destroy a session and unregister all its tools */ destroySession(sessionId: string): boolean { const session = this.sessions.get(sessionId); if (!session) { return false; } // Unregister all tools for this session const sessionTools = this.sessionTools.get(sessionId); if (sessionTools) { for (const toolName of sessionTools) { this.tools.delete(toolName); this.emit('tool_unregistered', toolName, sessionId); } this.sessionTools.delete(sessionId); } this.sessions.delete(sessionId); this.emit('session_destroyed', sessionId, session.type); return true; } /** * Update session last_used timestamp */ touchSession(sessionId: string): void { const session = this.sessions.get(sessionId); if (session) { session.last_used = new Date().toISOString(); } } /** * Get a tool by name */ getTool(name: string): RegisteredTool | undefined { return this.tools.get(name); } /** * Get all available tools */ getAllTools(): MCPTool[] { return Array.from(this.tools.values()).map(rt => rt.tool); } /** * Get tools for a specific session */ getSessionTools(sessionId: string): MCPTool[] { const toolNames = this.sessionTools.get(sessionId); if (!toolNames) { return []; } return Array.from(toolNames) .map(name => this.tools.get(name)) .filter((rt): rt is RegisteredTool => rt !== undefined) .map(rt => rt.tool); } /** * Check if a session exists */ hasSession(sessionId: string): boolean { return this.sessions.has(sessionId); } /** * Get session info */ getSession(sessionId: string): SessionInfo | undefined { return this.sessions.get(sessionId); } /** * Get all active sessions */ getAllSessions(): SessionInfo[] { return Array.from(this.sessions.values()); } /** * Execute a tool with proper context */ async executeTool(name: string, params: any, requestId: string): Promise<any> { const registeredTool = this.tools.get(name); if (!registeredTool) { throw new Error(`Tool '${name}' not found`); } // Update session last_used if this is a session tool if (registeredTool.session_id) { this.touchSession(registeredTool.session_id); } const context = { session_id: registeredTool.session_id, request_id: requestId }; return await registeredTool.handler(params, context); } /** * Clean up expired sessions (older than ttlMs) */ cleanupExpiredSessions(ttlMs: number = 24 * 60 * 60 * 1000): number { const now = Date.now(); const expired: string[] = []; for (const [sessionId, session] of this.sessions) { const lastUsed = new Date(session.last_used).getTime(); if (now - lastUsed > ttlMs) { expired.push(sessionId); } } for (const sessionId of expired) { this.destroySession(sessionId); } return expired.length; } /** * Get registry statistics */ getStats(): { total_tools: number; static_tools: number; session_tools: number; active_sessions: number; tools_by_session: Record<string, number>; } { const staticTools = Array.from(this.tools.values()).filter(t => !t.session_id).length; const sessionTools = Array.from(this.tools.values()).filter(t => t.session_id).length; const toolsBySession: Record<string, number> = {}; for (const [sessionId, toolNames] of this.sessionTools) { toolsBySession[sessionId] = toolNames.size; } return { total_tools: this.tools.size, static_tools: staticTools, session_tools: sessionTools, active_sessions: this.sessions.size, tools_by_session: toolsBySession }; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/JacobFV/mcp-fullstack'

If you have feedback or need assistance with the MCP directory API, please join our Discord server