Skip to main content
Glama
NorthSeacoder

Frontend Test Generation & Code Review MCP Server

metrics.ts4.94 kB
/** * Lightweight metrics facade used across the MCP server. * * Goals: * - Provide a consistent interface without forcing a concrete backend * - Enable integration with remote tracking services * - Offer a zero-dependency in-memory implementation by default */ import { logger } from './logger.js'; import type { MCPTrackingService } from './tracking-service.js'; export type MetricValue = number; export interface MetricLabels { [key: string]: string | number | boolean | undefined; } export interface MetricsClient { recordCounter(name: string, value?: MetricValue, labels?: MetricLabels): void; recordTimer(name: string, durationMs: MetricValue, labels?: MetricLabels): void; recordHistogram(name: string, value: MetricValue, labels?: MetricLabels): void; recordGauge(name: string, value: MetricValue, labels?: MetricLabels): void; export(): MetricSnapshot[]; reset(): void; } export interface MetricSnapshot { name: string; type: 'counter' | 'timer' | 'histogram' | 'gauge'; value: MetricValue; labels?: MetricLabels; timestamp: number; } /** * Default in-memory implementation used unless an external client is provided. */ export class InMemoryMetricsClient implements MetricsClient { private records: MetricSnapshot[] = []; private readonly logMetrics: boolean; constructor(options: { logMetrics?: boolean } = {}) { this.logMetrics = options.logMetrics ?? false; } recordCounter(name: string, value: MetricValue = 1, labels?: MetricLabels): void { this.pushRecord('counter', name, value, labels); } recordTimer(name: string, durationMs: MetricValue, labels?: MetricLabels): void { this.pushRecord('timer', name, durationMs, labels); } recordHistogram(name: string, value: MetricValue, labels?: MetricLabels): void { this.pushRecord('histogram', name, value, labels); } recordGauge(name: string, value: MetricValue, labels?: MetricLabels): void { this.pushRecord('gauge', name, value, labels); } export(): MetricSnapshot[] { return [...this.records]; } reset(): void { this.records = []; } protected pushRecord(type: MetricSnapshot['type'], name: string, value: MetricValue, labels?: MetricLabels) { const snapshot: MetricSnapshot = { name, type, value, labels, timestamp: Date.now(), }; this.records.push(snapshot); if (this.logMetrics) { logger.debug('[metrics]', snapshot); } } } class TrackingMetricsClient extends InMemoryMetricsClient { constructor(private readonly tracker: MCPTrackingService, options?: { logMetrics?: boolean }) { super(options); } override recordCounter(name: string, value: MetricValue = 1, labels?: MetricLabels): void { super.recordCounter(name, value, labels); this.trackMetric('counter', name, value, labels); } override recordTimer(name: string, durationMs: MetricValue, labels?: MetricLabels): void { super.recordTimer(name, durationMs, labels); this.trackMetric('timer', name, durationMs, labels); } override recordHistogram(name: string, value: MetricValue, labels?: MetricLabels): void { super.recordHistogram(name, value, labels); this.trackMetric('histogram', name, value, labels); } override recordGauge(name: string, value: MetricValue, labels?: MetricLabels): void { super.recordGauge(name, value, labels); this.trackMetric('gauge', name, value, labels); } private trackMetric(type: MetricSnapshot['type'], name: string, value: MetricValue, labels?: MetricLabels) { void this.tracker.track( { eventType: 'metric_recorded', metricType: type, metricName: name, value, labels: labels ?? null, }, 'INFO', `metric:${name}` ); } } let client: MetricsClient | undefined; export function initializeMetrics(customClient?: MetricsClient, trackingService?: MCPTrackingService): void { if (customClient) { client = customClient; } else if (trackingService) { client = new TrackingMetricsClient(trackingService, { logMetrics: process.env.LOG_METRICS === 'true' }); } else { client = new InMemoryMetricsClient({ logMetrics: process.env.LOG_METRICS === 'true' }); } logger.info('Metrics client initialized', { client: client.constructor.name }); } export function getMetrics(): MetricsClient { if (!client) { initializeMetrics(); } return client!; } /** * Helper to measure async execution time. */ export async function withTimer<T>( metricName: string, labels: MetricLabels | undefined, fn: () => Promise<T> ): Promise<T> { const startedAt = Date.now(); try { const result = await fn(); getMetrics().recordTimer(metricName, Date.now() - startedAt, { ...labels, status: 'success' }); return result; } catch (error) { getMetrics().recordTimer(metricName, Date.now() - startedAt, { ...labels, status: 'error' }); throw error; } }

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/NorthSeacoder/fe-testgen-mcp'

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