Skip to main content
Glama

hypertool-mcp

registry.tsโ€ข7.15 kB
/** * Application registry management */ import { promises as fs } from "fs"; import { vol } from "memfs"; import { isTestMode } from "../../config/environment.js"; import { join } from "path"; import { homedir, platform } from "os"; import { ApplicationRegistry, ApplicationDefinition, PlatformConfig, } from "../types/index.js"; export class AppRegistry { private registryPath: string; private registry: ApplicationRegistry | null = null; private fs: typeof fs; private basePath: string; constructor(basePath: string = join(homedir(), ".toolprint/hypertool-mcp")) { this.basePath = basePath; this.registryPath = join(basePath, "apps/registry.json"); // Use memfs in test mode, real fs in production this.fs = isTestMode() ? (vol.promises as any as typeof fs) : fs; } /** * Initialize the registry with default applications */ private createDefaultRegistry(): ApplicationRegistry { return { version: "1.0.0", applications: { "claude-desktop": { name: "Claude Desktop", enabled: true, platforms: { darwin: { configPath: "~/Library/Application Support/Claude/claude_desktop_config.json", format: "standard", }, win32: { configPath: "%APPDATA%\\Claude\\claude_desktop_config.json", format: "standard", }, }, detection: { type: "directory", path: this.getCurrentPlatform() === "darwin" ? "~/Library/Application Support/Claude" : "%APPDATA%\\Claude", }, }, cursor: { name: "Cursor IDE", enabled: true, platforms: { all: { configPath: "~/.cursor/mcp.json", format: "standard", }, }, detection: { type: "directory", path: "~/.cursor", }, }, "claude-code": { name: "Claude Code", enabled: true, platforms: { all: { configPath: "~/.claude.json", format: "custom", transformer: "claude-code", }, }, detection: { type: "file", path: "~/.claude.json", }, }, }, }; } /** * Load the application registry */ async load(): Promise<ApplicationRegistry> { if (this.registry) { return this.registry; } try { const content = await this.fs.readFile(this.registryPath, "utf-8"); this.registry = JSON.parse(content); return this.registry!; } catch (error) { // If file doesn't exist, create default registry if ((error as any).code === "ENOENT") { this.registry = this.createDefaultRegistry(); await this.save(); return this.registry; } throw error; } } /** * Save the application registry */ async save(): Promise<void> { if (!this.registry) { throw new Error("No registry loaded"); } // Ensure directory exists const dir = join(this.registryPath, ".."); await this.fs.mkdir(dir, { recursive: true }); // Write registry await this.fs.writeFile( this.registryPath, JSON.stringify(this.registry, null, 2), "utf-8" ); } /** * Get all enabled applications */ async getEnabledApplications(): Promise< Record<string, ApplicationDefinition> > { const registry = await this.load(); const enabled: Record<string, ApplicationDefinition> = {}; for (const [id, app] of Object.entries(registry.applications)) { if (app.enabled) { enabled[id] = app; } } return enabled; } /** * Get application by ID */ async getApplication(appId: string): Promise<ApplicationDefinition | null> { const registry = await this.load(); return registry.applications[appId] || null; } /** * Add or update an application */ async setApplication( appId: string, app: ApplicationDefinition ): Promise<void> { const registry = await this.load(); registry.applications[appId] = app; await this.save(); } /** * Remove an application */ async removeApplication(appId: string): Promise<void> { const registry = await this.load(); delete registry.applications[appId]; await this.save(); } /** * Get current platform (for testing support) */ private getCurrentPlatform(): NodeJS.Platform { // In test mode, check for simulated platform if (isTestMode()) { const testPlatform = (global as any).__TEST_PLATFORM__; if (testPlatform) { return testPlatform; } } return platform() as NodeJS.Platform; } /** * Get platform-specific configuration for an application */ getPlatformConfig(app: ApplicationDefinition): PlatformConfig | null { // Use test platform if available, otherwise use actual platform const currentPlatform = this.getCurrentPlatform(); // Check for platform-specific config first if (currentPlatform === "darwin" && app.platforms.darwin) { return app.platforms.darwin; } else if (currentPlatform === "linux" && app.platforms.linux) { return app.platforms.linux; } else if (currentPlatform === "win32" && app.platforms.win32) { return app.platforms.win32; } // Fall back to 'all' if available if (app.platforms.all) { return app.platforms.all; } return null; } /** * Resolve path with home directory and environment variables */ resolvePath(path: string): string { // Replace ~ with home directory (use test base directory in test mode) if (path.startsWith("~")) { const homeDir = isTestMode() ? this.basePath.replace("/.toolprint/hypertool-mcp", "") : homedir(); path = join(homeDir, path.slice(1)); } // Replace environment variables (Windows style %VAR%) path = path.replace(/%([^%]+)%/g, (_, varName) => { return process.env[varName] || ""; }); // Replace environment variables (Unix style $VAR or ${VAR}) path = path.replace(/\$\{?([A-Z_][A-Z0-9_]*)\}?/gi, (_, varName) => { return process.env[varName] || ""; }); return path; } /** * Detect if an application is installed */ async isApplicationInstalled(app: ApplicationDefinition): Promise<boolean> { const detection = app.detection; switch (detection.type) { case "file": case "directory": if (!detection.path) return false; const resolvedPath = this.resolvePath(detection.path); try { await this.fs.access(resolvedPath); return true; } catch { return false; } case "project-local": // Project-local apps are always "installed" if we're checking them return true; case "command": // TODO: Implement command-based detection return false; default: return false; } } }

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/toolprint/hypertool-mcp'

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