Skip to main content
Glama

Ultra MCP

service.ts4.75 kB
import { PricingFetcher } from './fetcher'; import { PricingCalculator } from './calculator'; import { ModelPricing, PricingData, CostCalculation } from './types'; export class PricingService { private static instance: PricingService; private fetcher: PricingFetcher; private calculator: PricingCalculator; private pricingDataCache: PricingData | null = null; private lastFetchTime: number = 0; private readonly MEMORY_CACHE_TTL = 5 * 60 * 1000; // 5 minutes in-memory cache private constructor() { this.fetcher = new PricingFetcher(); this.calculator = new PricingCalculator(); } static getInstance(): PricingService { if (!PricingService.instance) { PricingService.instance = new PricingService(); } return PricingService.instance; } /** * Get pricing data with in-memory caching for performance */ private async getPricingData(forceRefresh: boolean = false): Promise<PricingData> { const now = Date.now(); // Check in-memory cache first if (!forceRefresh && this.pricingDataCache && (now - this.lastFetchTime) < this.MEMORY_CACHE_TTL) { return this.pricingDataCache; } // Fetch from file cache or remote this.pricingDataCache = await this.fetcher.getLatestPricing(forceRefresh); this.lastFetchTime = now; return this.pricingDataCache; } /** * Get pricing for a specific model */ async getModelPricing(model: string): Promise<ModelPricing | null> { try { const pricingData = await this.getPricingData(); const normalizedModel = this.calculator.normalizeModelName(model); // Try exact match first if (pricingData[normalizedModel]) { return pricingData[normalizedModel]; } // Try to find by partial match for (const [key, value] of Object.entries(pricingData)) { if (key.toLowerCase() === normalizedModel.toLowerCase()) { return value; } } // Model not found console.warn(`Pricing not found for model: ${model} (normalized: ${normalizedModel})`); return null; } catch (error) { console.error('Error getting model pricing:', error); return null; } } /** * Calculate cost for a model with given token usage */ async calculateCost( model: string, inputTokens: number, outputTokens: number ): Promise<CostCalculation | null> { const pricing = await this.getModelPricing(model); if (!pricing) { // Return a default calculation with zero cost if pricing not found return { inputCost: 0, outputCost: 0, totalCost: 0, inputTokens, outputTokens, model, tieredPricingApplied: false, }; } return this.calculator.calculateCostFromPricing( model, pricing, inputTokens, outputTokens ); } /** * Refresh the pricing cache */ async refreshCache(): Promise<void> { this.pricingDataCache = null; this.lastFetchTime = 0; await this.getPricingData(true); } /** * Get cache information */ async getCacheInfo(): Promise<{ exists: boolean; age?: number; expired?: boolean; lastInMemoryFetch?: number; }> { const cacheInfo = await this.fetcher.getCacheInfo(); return { exists: cacheInfo?.exists ?? false, age: cacheInfo?.age, expired: cacheInfo?.expired, lastInMemoryFetch: this.lastFetchTime ? Date.now() - this.lastFetchTime : undefined, }; } /** * Clear all caches */ async clearCache(): Promise<void> { this.pricingDataCache = null; this.lastFetchTime = 0; await this.fetcher.clearCache(); } /** * Get all available models with pricing */ async getAllModels(): Promise<string[]> { try { const pricingData = await this.getPricingData(); return Object.keys(pricingData).sort(); } catch (error) { console.error('Error getting all models:', error); return []; } } /** * Check if a model has pricing information */ async hasModelPricing(model: string): Promise<boolean> { const pricing = await this.getModelPricing(model); return pricing !== null; } /** * Get a formatted cost estimate */ async getFormattedCostEstimate( model: string, inputTokens: number, outputTokens: number ): Promise<string | null> { const calculation = await this.calculateCost(model, inputTokens, outputTokens); if (!calculation) { return null; } return this.calculator.getCostEstimateMessage(calculation); } /** * Format a cost value */ formatCost(cost: number): string { return this.calculator.formatCost(cost); } }

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/RealMikeChong/ultra-mcp'

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