Skip to main content
Glama
metrics.ts9.54 kB
/** * @module metrics * @packageDocumentation * * Registers and updates Prometheus metrics via [prom-client]. * * Metrics endpoint exposed on /metrics (ex. curl -H "x-api-key:test" -GET localhost:3002/api/metrics). * * [prometheus]: https://prometheus.io/docs/introduction/overview/ * [prom-client]: https://github.com/siimon/prom-client */ import client from 'prom-client'; /** * Singleton class managing Prometheus metrics. */ const DEFAULT_BUCKETS = [ 0.001, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 1, 2, 5, 10, 15, 30, 60, 120, ]; class Metrics { /** * Agent metrics */ private agentCountActive?: client.Gauge; private agentCountTotal?: client.Counter; // more for debugging, and to know how many agents were created private agentResponseTime?: client.Histogram; private dbQueryTime?: client.Histogram; private agentToolUseCounter = new Map<string, client.Counter>(); private agentMsgTotal = new Map<string, client.Counter>(); // total messages sent by 1 agent private agentPromptTokens?: client.Counter; private agentCompletionTokens?: client.Counter; private agentTotalTokens?: client.Counter; /** * User metrics, TODO: User Management incoming */ private userAgentsActive?: client.Gauge; private userAgentsTotal?: client.Counter; private userPromptTokens?: client.Counter; private userCompletionTokens?: client.Counter; private userTotalTokens?: client.Counter; /** * Error metrics / or blackbox exporter ? */ private errorCount = new Map<string, client.Counter>(); // total errors by type private registered = false; public get contentType() { return client.register.contentType; } /** * Return the dump text of Prometheus metrics. * * @returns Plaintext Prometheus format. */ public async metrics(): Promise<string> { if (!this.registered) { this.register(); } return client.register.metrics(); } private register(): void { if (this.registered) return; this.registered = true; client.collectDefaultMetrics({ prefix: 'nodejs_' }); this.agentCountActive = new client.Gauge({ name: 'agent_count_active', help: 'Number of currently active agents', }); this.agentCountTotal = new client.Counter({ name: 'agent_count_total', help: 'Number of agents created since server start', }); this.agentResponseTime = new client.Histogram({ name: 'agent_response_time_seconds', help: 'Time agents take to response to API requests, in seconds', labelNames: ['agent', 'mode', 'route'] as const, buckets: DEFAULT_BUCKETS, }); this.dbQueryTime = new client.Histogram({ name: 'db_response_time_seconds', help: 'Time the database takes to respond to queries, in seconds', labelNames: ['query'] as const, buckets: DEFAULT_BUCKETS, }); this.agentPromptTokens = new client.Counter({ name: 'agent_prompt_tokens_total', help: 'Total prompt tokens used per agent', labelNames: ['agent'] as const, }); this.agentCompletionTokens = new client.Counter({ name: 'agent_completion_tokens_total', help: 'Total completion tokens generated per agent', labelNames: ['agent'] as const, }); this.agentTotalTokens = new client.Counter({ name: 'agent_tokens_total', help: 'Total tokens (prompt + completion) used per agent', labelNames: ['agent'] as const, }); this.userAgentsActive = new client.Gauge({ name: 'user_agent_active', help: 'Number of active agents per user', labelNames: ['user', 'agent', 'mode'] as const, }); this.userAgentsTotal = new client.Counter({ name: 'user_agent_total', help: 'Total number of agent sessions started per user', labelNames: ['user', 'agent', 'mode'] as const, }); this.userPromptTokens = new client.Counter({ name: 'user_prompt_tokens_total', help: 'Total prompt tokens used per user', labelNames: ['user', 'agent'] as const, }); this.userCompletionTokens = new client.Counter({ name: 'user_completion_tokens_total', help: 'Total completion tokens used per user', labelNames: ['user', 'agent'] as const, }); this.userTotalTokens = new client.Counter({ name: 'user_tokens_total', help: 'Total tokens (prompt + completion) used per user', labelNames: ['user', 'agent'] as const, }); } public agentConnect(): void { if (!this.agentCountActive) this.register(); this.agentCountActive!.inc(); this.agentCountTotal!.inc(); } public agentDisconnect(): void { if (!this.agentCountActive) this.register(); this.agentCountActive!.dec(); } /** * Measure the response time of an agent's API request. * * @param agent - Agent name * @param mode - Connection mode (e.g., 'web', 'mobile') * @param route - API route being accessed * @param f - Function that returns a Promise for the agent's response * @returns Result of the agent's response */ public async agentResponseTimeMeasure<T>( agent: string, mode: string, route: string, f: Promise<T> ): Promise<T> { if (!this.agentResponseTime) this.register(); const end = this.agentResponseTime!.startTimer({ agent, mode, route }); const res = await f; end(); // count around minus 2secs return res; } /** * Measure the response time of async chunks. * * @param agent - Agent ID * @param mode - 'websocket' ... * @param route - Route * @param genFn - function that return AsyncGenerator<T> * @returns Un AsyncGenerator<T> identique, en mesurant le temps jusqu’à la fin du flux. */ public async *agentResponseTimeStream<T>( agent: string, mode: string, route: string, genFn: () => AsyncGenerator<T> ): AsyncGenerator<T> { if (!this.agentResponseTime) this.register(); const end = this.agentResponseTime!.startTimer(); try { for await (const chunk of genFn()) { yield chunk; } } finally { end({ agent, mode, route }); } } /** * Measure the execution time of database request. Monitoring performance of database queries. * * @param query - Database query string * @param f - Function that returns a Promise for the database operation * @returns */ public async dbResponseTime<T>( query: string, fn: () => Promise<T> ): Promise<T> { if (!this.dbQueryTime) this.register(); const end = this.dbQueryTime!.startTimer({ query }); try { return await fn(); } finally { end(); } } public agentMsgCount(agent: string, mode: string, msgType: string): void { const counter = this.agentMsgTotal.get(msgType) || (() => { const c = new client.Counter({ name: `agent_${msgType}_messages_total`, help: 'Total messages sent by an agent', labelNames: ['agent', 'mode'] as const, }); this.agentMsgTotal.set(msgType, c); return c; })(); counter.labels({ agent, mode }).inc(); } public agentToolUseCount(agent: string, mode: string, tool: string): void { if (!this.agentCountActive) this.register(); const counter = this.agentToolUseCounter.get(tool) || (() => { const c = new client.Counter({ name: `tool_${tool}_use_counter`, help: 'Number of times an agent uses this tool', labelNames: ['agent', 'mode'] as const, }); this.agentToolUseCounter.set(tool, c); return c; })(); counter.labels({ agent, mode }).inc(); } /** * Records token usage for an agent. * @param agent - Agent identifier * @param promptTokens - Number of prompt tokens used * @param completionTokens - Number of completion tokens generated */ public recordAgentTokenUsage( agent: string, promptTokens: number, completionTokens: number ): void { if (!this.agentPromptTokens) this.register(); this.agentPromptTokens!.labels(agent).inc(promptTokens); this.agentCompletionTokens!.labels(agent).inc(completionTokens); this.agentTotalTokens!.labels(agent).inc(promptTokens + completionTokens); } /** * Sets the number of active agents for a user. * @param user - User identifier * @param count - Number of active agents */ public setUserActiveAgents(user: string, count: number): void { if (!this.userAgentsActive) this.register(); this.userAgentsActive!.set({ user }, count); } public userAgentConnect(user: string, agent: string, mode: string): void { if (!this.userAgentsActive) this.register(); this.userAgentsActive!.labels({ user, agent, mode }).inc(); this.userAgentsTotal!.labels({ user, agent, mode }).inc(); } public userAgentDisconnect(user: string, agent: string, mode: string): void { if (!this.userAgentsActive) this.register(); this.userAgentsActive!.labels({ user, agent, mode }).dec(); } public userTokenUsage( user: string, agent: string, promptTokens: number, completionTokens: number ): void { if (!this.userPromptTokens) this.register(); this.userPromptTokens!.labels({ user, agent }).inc(promptTokens); this.userCompletionTokens!.labels({ user, agent }).inc(completionTokens); this.userTotalTokens!.labels({ user, agent }).inc( promptTokens + completionTokens ); } } const metrics = new Metrics(); export default metrics; export { metrics };

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/KasarLabs/snak'

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