Skip to main content
Glama

CTS MCP Server

by EricA1019
tool-wrapper.test.ts28.7 kB
/**/**/** * Tests for Tool Wrapper * Following Quinn's comprehensive testing methodology * Tests for Tool Wrapper * Tests for Tool Wrapper */ * Following Quinn's comprehensive testing methodology * Following Quinn's comprehensive testing methodology import { describe, it, expect, beforeEach, jest, afterEach } from '@jest/globals'; import { wrapToolHandler, withCache, withObservability } from '../tool-wrapper.js'; */ */ import { globalCache } from '../../cache/result-cache.js'; import { metrics, type ToolMetrics } from '../../observability/index.js'; import type { ToolHandler } from '../../types.js'; import { describe, it, expect, beforeEach, jest, afterEach } from '@jest/globals';import { describe, it, expect, beforeEach, jest, afterEach } from '@jest/globals'; describe('Tool Wrapper', () => { beforeEach(() => {import { wrapToolHandler, withCache, withObservability } from '../tool-wrapper.js';import { wrapToolHandler, withCache, withObservability } from '../tool-wrapper.js'; jest.clearAllMocks(); globalCache.clear();import { globalCache } from '../../cache/result-cache.js';import { globalCache } from '../../cache/result-cache.js'; metrics.reset(); });import { metrics, type ToolMetrics } from '../../observability/index.js';import { metrics, type ToolMetrics } from '../../observability/index.js'; afterEach(() => {import type { ToolHandler } from '../../types.js';import type { ToolHandler } from '../../types.js'; jest.restoreAllMocks(); }); describe('wrapToolHandler', () => {describe('Tool Wrapper', () => {// Type for test results with cache metadata it('should execute tool handler and record metrics', async () => { // Arrange beforeEach(() => {type TestResult = Record<string, any> & { cached?: boolean }; const handler = jest.fn<ToolHandler>().mockResolvedValue({ success: true, data: 'test' }); const wrapped = wrapToolHandler(handler, { toolName: 'TestTool', cacheable: false }); jest.clearAllMocks(); // Act globalCache.clear();describe('Tool Wrapper', () => { const result = await wrapped({ arg1: 'value1' }); metrics.reset(); beforeEach(() => { // Assert expect(handler).toHaveBeenCalledWith({ arg1: 'value1' }); }); jest.clearAllMocks(); expect(result).toEqual({ success: true, data: 'test' }); globalCache.clear(); const toolMetrics = metrics.getToolMetrics('TestTool') as ToolMetrics; expect(toolMetrics.executionCount).toBe(1); afterEach(() => { metrics.reset(); expect(toolMetrics.errorCount).toBe(0); }); jest.restoreAllMocks(); }); it('should handle tool execution errors', async () => { }); // Arrange const error = new Error('Tool failed'); afterEach(() => { const handler = jest.fn<ToolHandler>().mockRejectedValue(error); const wrapped = wrapToolHandler(handler, { toolName: 'TestTool', cacheable: false }); describe('wrapToolHandler', () => { jest.restoreAllMocks(); // Act & Assert it('should execute tool handler and record metrics', async () => { }); await expect(wrapped({ arg1: 'value1' })).rejects.toThrow('Tool failed'); // Arrange const toolMetrics = metrics.getToolMetrics('TestTool') as ToolMetrics; expect(toolMetrics.executionCount).toBe(1); const handler = jest.fn<ToolHandler>().mockResolvedValue({ success: true, data: 'test' }); describe('wrapToolHandler', () => { expect(toolMetrics.errorCount).toBe(1); }); const wrapped = wrapToolHandler(handler, { toolName: 'TestTool', cacheable: false }); it('should execute tool handler and record metrics', async () => { it('should record execution duration', async () => { // Arrange // Arrange const handler = jest.fn<ToolHandler>().mockImplementation(async () => { // Act const handler = jest.fn<ToolHandler>().mockResolvedValue({ success: true, data: 'test' }); await new Promise(resolve => setTimeout(resolve, 10)); return { success: true }; const result = await wrapped({ arg1: 'value1' }); const wrapped = wrapToolHandler(handler, { toolName: 'TestTool', cacheable: false }); }); const wrapped = wrapToolHandler(handler, { toolName: 'TestTool', cacheable: false }); // Act // Assert // Act await wrapped({}); expect(handler).toHaveBeenCalledWith({ arg1: 'value1' }); const result = await wrapped({ arg1: 'value1' }); // Assert const toolMetrics = metrics.getToolMetrics('TestTool') as ToolMetrics; expect(result).toEqual({ success: true, data: 'test' }); expect(toolMetrics.averageDuration).toBeGreaterThan(5); // At least 5ms }); // Assert }); const toolMetrics = metrics.getToolMetrics('TestTool') as ToolMetrics; expect(handler).toHaveBeenCalledWith({ arg1: 'value1' }); describe('Caching', () => { it('should cache results when cacheable=true', async () => { expect(toolMetrics.executionCount).toBe(1); expect(result).toEqual({ success: true, data: 'test' }); // Arrange let callCount = 0; expect(toolMetrics.errorCount).toBe(0); const handler = jest.fn<ToolHandler>().mockImplementation(async () => { callCount++; }); const toolMetrics = metrics.getToolMetrics('TestTool') as ToolMetrics; return { success: true, data: `call_${callCount}` }; }); expect(toolMetrics.executionCount).toBe(1); const wrapped = wrapToolHandler(handler, { toolName: 'CacheableTool', cacheable: true }); const args = { projectPath: '/path/to/project' }; it('should handle tool execution errors', async () => { expect(toolMetrics.errorCount).toBe(0); // Act // Arrange }); const result1 = await wrapped(args); const result2 = await wrapped(args); const error = new Error('Tool failed'); // Assert const handler = jest.fn<ToolHandler>().mockRejectedValue(error); it('should handle tool execution errors', async () => { expect(handler).toHaveBeenCalledTimes(1); // Only called once expect((result1 as any).data).toBe('call_1'); const wrapped = wrapToolHandler(handler, { toolName: 'TestTool', cacheable: false }); // Arrange expect((result2 as any).data).toBe('call_1'); // Same result from cache expect((result1 as any).cached).toBe(false); const error = new Error('Tool failed'); expect((result2 as any).cached).toBe(true); // Act & Assert const handler = jest.fn<ToolHandler>().mockRejectedValue(error); const toolMetrics = metrics.getToolMetrics('CacheableTool') as ToolMetrics; expect(toolMetrics.cacheHitRate).toBe(0.5); // 1 hit, 1 miss await expect(wrapped({ arg1: 'value1' })).rejects.toThrow('Tool failed'); const wrapped = wrapToolHandler(handler, { toolName: 'TestTool', cacheable: false }); }); it('should not cache when cacheable=false', async () => { // Arrange const toolMetrics = metrics.getToolMetrics('TestTool') as ToolMetrics; // Act & Assert let callCount = 0; const handler = jest.fn<ToolHandler>().mockImplementation(async () => { expect(toolMetrics.executionCount).toBe(1); await expect(wrapped({ arg1: 'value1' })).rejects.toThrow('Tool failed'); callCount++; return { success: true, data: `call_${callCount}` }; expect(toolMetrics.errorCount).toBe(1); }); const wrapped = wrapToolHandler(handler, { toolName: 'NonCacheableTool', cacheable: false }); }); const toolMetrics = metrics.getToolMetrics('TestTool') as ToolMetrics; const args = { projectPath: '/path/to/project' }; expect(toolMetrics.executionCount).toBe(1); // Act const result1 = await wrapped(args); it('should record execution duration', async () => { expect(toolMetrics.errorCount).toBe(1); const result2 = await wrapped(args); // Arrange }); // Assert expect(handler).toHaveBeenCalledTimes(2); // Called twice const handler = jest.fn<ToolHandler>().mockImplementation(async () => { expect((result1 as any).data).toBe('call_1'); expect((result2 as any).data).toBe('call_2'); // Different result await new Promise(resolve => setTimeout(resolve, 10)); it('should record execution duration', async () => { }); return { success: true }; // Arrange it('should bypass cache when _bypassCache=true', async () => { // Arrange }); const handler = jest.fn<ToolHandler>().mockImplementation(async () => { let callCount = 0; const handler = jest.fn<ToolHandler>().mockImplementation(async () => { const wrapped = wrapToolHandler(handler, { toolName: 'TestTool', cacheable: false }); await new Promise(resolve => setTimeout(resolve, 10)); callCount++; return { success: true, data: `call_${callCount}` }; return { success: true }; }); const wrapped = wrapToolHandler(handler, { toolName: 'CacheableTool', cacheable: true }); // Act }); // Act await wrapped({}); const wrapped = wrapToolHandler(handler, { toolName: 'TestTool', cacheable: false }); const result1 = await wrapped({ projectPath: '/path' }); const result2 = await wrapped({ projectPath: '/path', _bypassCache: true }); const result3 = await wrapped({ projectPath: '/path' }); // Assert // Act // Assert expect(handler).toHaveBeenCalledTimes(2); // Called twice (bypass skips cache) const toolMetrics = metrics.getToolMetrics('TestTool') as ToolMetrics; await wrapped({}); expect((result1 as any).data).toBe('call_1'); expect((result2 as any).data).toBe('call_2'); // Bypassed cache expect(toolMetrics.averageDuration).toBeGreaterThan(5); // At least 5ms expect((result3 as any).data).toBe('call_1'); // From cache (first call) }); }); // Assert it('should remove _bypassCache from args before processing', async () => { }); const toolMetrics = metrics.getToolMetrics('TestTool'); // Arrange const handler = jest.fn<ToolHandler>().mockResolvedValue({ success: true }); expect(toolMetrics.averageDuration).toBeGreaterThan(5); // At least 5ms const wrapped = wrapToolHandler(handler, { toolName: 'TestTool', cacheable: true }); describe('Caching', () => { }); // Act await wrapped({ projectPath: '/path', _bypassCache: true }); it('should cache results when cacheable=true', async () => { }); // Assert // Arrange expect(handler).toHaveBeenCalledWith({ projectPath: '/path' }); expect(handler).not.toHaveBeenCalledWith(expect.objectContaining({ _bypassCache: true })); let callCount = 0; describe('Caching', () => { }); const handler = jest.fn<ToolHandler>().mockImplementation(async () => { it('should cache results when cacheable=true', async () => { it('should cache different results for different arguments', async () => { // Arrange callCount++; // Arrange const handler = jest.fn<ToolHandler>().mockImplementation(async (args: any) => { return { success: true, path: args.projectPath }; return { success: true, data: `call_${callCount}` }; let callCount = 0; }); const wrapped = wrapToolHandler(handler, { toolName: 'CacheableTool', cacheable: true }); }); const handler = jest.fn().mockImplementation(async () => { // Act const wrapped = wrapToolHandler(handler, { toolName: 'CacheableTool', cacheable: true }); callCount++; const result1 = await wrapped({ projectPath: '/path1' }); const result2 = await wrapped({ projectPath: '/path2' }); const args = { projectPath: '/path/to/project' }; return { success: true, data: `call_${callCount}` }; const result3 = await wrapped({ projectPath: '/path1' }); // Same as first }); // Assert expect(handler).toHaveBeenCalledTimes(2); // path1, path2 // Act const wrapped = wrapToolHandler(handler, { toolName: 'CacheableTool', cacheable: true }); expect((result1 as any).path).toBe('/path1'); expect((result2 as any).path).toBe('/path2'); const result1 = await wrapped(args); const args = { projectPath: '/path/to/project' }; expect((result3 as any).path).toBe('/path1'); // From cache expect((result3 as any).cached).toBe(true); const result2 = await wrapped(args); }); }); // Act describe('Convenience Functions', () => { // Assert const result1 = await wrapped(args); it('withCache should create cacheable handler', async () => { // Arrange expect(handler).toHaveBeenCalledTimes(1); // Only called once const result2 = await wrapped(args); let callCount = 0; const handler = jest.fn<ToolHandler>().mockImplementation(async () => { expect((result1 as any).data).toBe('call_1'); callCount++; return { success: true, data: `call_${callCount}` }; expect((result2 as any).data).toBe('call_1'); // Same result from cache // Assert }); const wrapped = withCache('CacheableTool', handler); expect((result1 as any).cached).toBe(false); expect(handler).toHaveBeenCalledTimes(1); // Only called once // Act expect((result2 as any).cached).toBe(true); expect(result1.data).toBe('call_1'); const result1 = await wrapped({ arg: 'value' }); const result2 = await wrapped({ arg: 'value' }); expect(result2.data).toBe('call_1'); // Same result from cache // Assert const toolMetrics = metrics.getToolMetrics('CacheableTool') as ToolMetrics; expect(result1.cached).toBe(false); expect(handler).toHaveBeenCalledTimes(1); expect((result2 as any).cached).toBe(true); expect(toolMetrics.cacheHitRate).toBe(0.5); // 1 hit, 1 miss expect(result2.cached).toBe(true); }); }); it('withObservability should create non-cacheable handler', async () => { // Arrange const toolMetrics = metrics.getToolMetrics('CacheableTool'); let callCount = 0; const handler = jest.fn<ToolHandler>().mockImplementation(async () => { it('should not cache when cacheable=false', async () => { expect(toolMetrics.cacheHitRate).toBe(0.5); // 1 hit, 1 miss callCount++; return { success: true, data: `call_${callCount}` }; // Arrange }); }); const wrapped = withObservability('NonCacheableTool', handler); let callCount = 0; // Act const handler = jest.fn<ToolHandler>().mockImplementation(async () => { it('should not cache when cacheable=false', async () => { const result1 = await wrapped({ arg: 'value' }); const result2 = await wrapped({ arg: 'value' }); callCount++; // Arrange // Assert return { success: true, data: `call_${callCount}` }; let callCount = 0; expect(handler).toHaveBeenCalledTimes(2); expect((result1 as any).data).toBe('call_1'); }); const handler = jest.fn().mockImplementation(async () => { expect((result2 as any).data).toBe('call_2'); }); const wrapped = wrapToolHandler(handler, { toolName: 'NonCacheableTool', cacheable: false }); callCount++; }); const args = { projectPath: '/path/to/project' }; return { success: true, data: `call_${callCount}` }; describe('Metrics Recording', () => { it('should record successful execution metrics', async () => { }); // Arrange const handler = jest.fn<ToolHandler>().mockResolvedValue({ success: true }); // Act const wrapped = wrapToolHandler(handler, { toolName: 'NonCacheableTool', cacheable: false }); const wrapped = wrapToolHandler(handler, { toolName: 'MetricsTool', cacheable: false }); const result1 = await wrapped(args); const args = { projectPath: '/path/to/project' }; // Act await wrapped({}); const result2 = await wrapped(args); // Assert // Act const toolMetrics = metrics.getToolMetrics('MetricsTool') as ToolMetrics; expect(toolMetrics.executionCount).toBe(1); // Assert const result1 = await wrapped(args); expect(toolMetrics.errorCount).toBe(0); expect(toolMetrics.averageDuration).toBeGreaterThan(0); expect(handler).toHaveBeenCalledTimes(2); // Called twice const result2 = await wrapped(args); }); expect((result1 as any).data).toBe('call_1'); it('should record failed execution metrics', async () => { // Arrange expect((result2 as any).data).toBe('call_2'); // Different result // Assert const handler = jest.fn<ToolHandler>().mockRejectedValue(new Error('Failed')); const wrapped = wrapToolHandler(handler, { toolName: 'MetricsTool', cacheable: false }); }); expect(handler).toHaveBeenCalledTimes(2); // Called twice // Act expect(result1.data).toBe('call_1'); try { await wrapped({}); it('should bypass cache when _bypassCache=true', async () => { expect(result2.data).toBe('call_2'); // Different result } catch (error) { // Expected // Arrange }); } let callCount = 0; // Assert const toolMetrics = metrics.getToolMetrics('MetricsTool') as ToolMetrics; const handler = jest.fn<ToolHandler>().mockImplementation(async () => { it('should bypass cache when _bypassCache=true', async () => { expect(toolMetrics.executionCount).toBe(1); expect(toolMetrics.errorCount).toBe(1); callCount++; // Arrange }); return { success: true, data: `call_${callCount}` }; let callCount = 0; it('should record cache hit rate', async () => { // Arrange }); const handler = jest.fn().mockImplementation(async () => { const handler = jest.fn<ToolHandler>().mockResolvedValue({ success: true }); const wrapped = wrapToolHandler(handler, { toolName: 'CacheTool', cacheable: true }); const wrapped = wrapToolHandler(handler, { toolName: 'CacheableTool', cacheable: true }); callCount++; // Act return { success: true, data: `call_${callCount}` }; await wrapped({ arg: 'value' }); // Miss await wrapped({ arg: 'value' }); // Hit // Act }); await wrapped({ arg: 'value' }); // Hit const result1 = await wrapped({ projectPath: '/path' }); const wrapped = wrapToolHandler(handler, { toolName: 'CacheableTool', cacheable: true }); // Assert const toolMetrics = metrics.getToolMetrics('CacheTool') as ToolMetrics; const result2 = await wrapped({ projectPath: '/path', _bypassCache: true }); expect(toolMetrics.cacheHitRate).toBeCloseTo(0.667, 2); // 2/3 hits }); const result3 = await wrapped({ projectPath: '/path' }); // Act }); }); const result1 = await wrapped({ projectPath: '/path' }); // Assert const result2 = await wrapped({ projectPath: '/path', _bypassCache: true }); expect(handler).toHaveBeenCalledTimes(2); // Called twice (bypass skips cache) const result3 = await wrapped({ projectPath: '/path' }); expect((result1 as any).data).toBe('call_1'); expect((result2 as any).data).toBe('call_2'); // Bypassed cache // Assert expect((result3 as any).data).toBe('call_1'); // From cache (first call) expect(handler).toHaveBeenCalledTimes(2); // Called twice (bypass skips cache) }); expect(result1.data).toBe('call_1'); expect(result2.data).toBe('call_2'); // Bypassed cache it('should remove _bypassCache from args before processing', async () => { expect(result3.data).toBe('call_1'); // From cache (first call) // Arrange }); const handler = jest.fn<ToolHandler>().mockResolvedValue({ success: true }); const wrapped = wrapToolHandler(handler, { toolName: 'TestTool', cacheable: true }); it('should remove _bypassCache from args before processing', async () => { // Arrange // Act const handler = jest.fn().mockResolvedValue({ success: true }); await wrapped({ projectPath: '/path', _bypassCache: true }); const wrapped = wrapToolHandler(handler, { toolName: 'TestTool', cacheable: true }); // Assert // Act expect(handler).toHaveBeenCalledWith({ projectPath: '/path' }); await wrapped({ projectPath: '/path', _bypassCache: true }); expect(handler).not.toHaveBeenCalledWith(expect.objectContaining({ _bypassCache: true })); }); // Assert expect(handler).toHaveBeenCalledWith({ projectPath: '/path' }); it('should cache different results for different arguments', async () => { expect(handler).not.toHaveBeenCalledWith(expect.objectContaining({ _bypassCache: true })); // Arrange }); const handler = jest.fn<ToolHandler>().mockImplementation(async (args: any) => { return { success: true, path: args.projectPath }; it('should cache different results for different arguments', async () => { }); // Arrange const wrapped = wrapToolHandler(handler, { toolName: 'CacheableTool', cacheable: true }); const handler = jest.fn().mockImplementation(async (args: any) => { return { success: true, path: args.projectPath }; // Act }); const result1 = await wrapped({ projectPath: '/path1' }); const wrapped = wrapToolHandler(handler, { toolName: 'CacheableTool', cacheable: true }); const result2 = await wrapped({ projectPath: '/path2' }); const result3 = await wrapped({ projectPath: '/path1' }); // Same as first // Act const result1 = await wrapped({ projectPath: '/path1' }); // Assert const result2 = await wrapped({ projectPath: '/path2' }); expect(handler).toHaveBeenCalledTimes(2); // path1, path2 const result3 = await wrapped({ projectPath: '/path1' }); // Same as first expect((result1 as any).path).toBe('/path1'); expect((result2 as any).path).toBe('/path2'); // Assert expect((result3 as any).path).toBe('/path1'); // From cache expect(handler).toHaveBeenCalledTimes(2); // path1, path2 expect((result3 as any).cached).toBe(true); expect(result1.path).toBe('/path1'); }); expect(result2.path).toBe('/path2'); }); expect(result3.path).toBe('/path1'); // From cache expect(result3.cached).toBe(true); describe('Convenience Functions', () => { }); it('withCache should create cacheable handler', async () => { }); // Arrange let callCount = 0; describe('Convenience Functions', () => { const handler = jest.fn<ToolHandler>().mockImplementation(async () => { it('withCache should create cacheable handler', async () => { callCount++; // Arrange return { success: true, data: `call_${callCount}` }; let callCount = 0; }); const handler = jest.fn().mockImplementation(async () => { const wrapped = withCache('CacheableTool', handler); callCount++; return { success: true, data: `call_${callCount}` }; // Act }); const result1 = await wrapped({ arg: 'value' }); const wrapped = withCache('CacheableTool', handler); const result2 = await wrapped({ arg: 'value' }); // Act // Assert const result1 = await wrapped({ arg: 'value' }); expect(handler).toHaveBeenCalledTimes(1); const result2 = await wrapped({ arg: 'value' }); expect((result2 as any).cached).toBe(true); }); // Assert expect(handler).toHaveBeenCalledTimes(1); it('withObservability should create non-cacheable handler', async () => { expect(result2.cached).toBe(true); // Arrange }); let callCount = 0; const handler = jest.fn<ToolHandler>().mockImplementation(async () => { it('withObservability should create non-cacheable handler', async () => { callCount++; // Arrange return { success: true, data: `call_${callCount}` }; let callCount = 0; }); const handler = jest.fn().mockImplementation(async () => { const wrapped = withObservability('NonCacheableTool', handler); callCount++; return { success: true, data: `call_${callCount}` }; // Act }); const result1 = await wrapped({ arg: 'value' }); const wrapped = withObservability('NonCacheableTool', handler); const result2 = await wrapped({ arg: 'value' }); // Act // Assert const result1 = await wrapped({ arg: 'value' }); expect(handler).toHaveBeenCalledTimes(2); const result2 = await wrapped({ arg: 'value' }); expect((result1 as any).data).toBe('call_1'); expect((result2 as any).data).toBe('call_2'); // Assert }); expect(handler).toHaveBeenCalledTimes(2); }); expect(result1.data).toBe('call_1'); expect(result2.data).toBe('call_2'); describe('Metrics Recording', () => { }); it('should record successful execution metrics', async () => { }); // Arrange const handler = jest.fn<ToolHandler>().mockResolvedValue({ success: true }); describe('Metrics Recording', () => { const wrapped = wrapToolHandler(handler, { toolName: 'MetricsTool', cacheable: false }); it('should record successful execution metrics', async () => { // Arrange // Act const handler = jest.fn().mockResolvedValue({ success: true }); await wrapped({}); const wrapped = wrapToolHandler(handler, { toolName: 'MetricsTool', cacheable: false }); // Assert // Act const toolMetrics = metrics.getToolMetrics('MetricsTool') as ToolMetrics; await wrapped({}); expect(toolMetrics.executionCount).toBe(1); expect(toolMetrics.errorCount).toBe(0); // Assert expect(toolMetrics.averageDuration).toBeGreaterThan(0); const toolMetrics = metrics.getToolMetrics('MetricsTool'); }); expect(toolMetrics.executionCount).toBe(1); expect(toolMetrics.errorCount).toBe(0); it('should record failed execution metrics', async () => { expect(toolMetrics.averageDuration).toBeGreaterThan(0); // Arrange }); const handler = jest.fn<ToolHandler>().mockRejectedValue(new Error('Failed')); const wrapped = wrapToolHandler(handler, { toolName: 'MetricsTool', cacheable: false }); it('should record failed execution metrics', async () => { // Arrange // Act const handler = jest.fn().mockRejectedValue(new Error('Failed')); try { const wrapped = wrapToolHandler(handler, { toolName: 'MetricsTool', cacheable: false }); await wrapped({}); } catch (error) { // Act // Expected try { } await wrapped({}); } catch (error) { // Assert // Expected const toolMetrics = metrics.getToolMetrics('MetricsTool') as ToolMetrics; } expect(toolMetrics.executionCount).toBe(1); expect(toolMetrics.errorCount).toBe(1); // Assert }); const toolMetrics = metrics.getToolMetrics('MetricsTool'); expect(toolMetrics.executionCount).toBe(1); it('should record cache hit rate', async () => { expect(toolMetrics.errorCount).toBe(1); // Arrange }); const handler = jest.fn<ToolHandler>().mockResolvedValue({ success: true }); const wrapped = wrapToolHandler(handler, { toolName: 'CacheTool', cacheable: true }); it('should record cache hit rate', async () => { // Arrange // Act const handler = jest.fn().mockResolvedValue({ success: true }); await wrapped({ arg: 'value' }); // Miss const wrapped = wrapToolHandler(handler, { toolName: 'CacheTool', cacheable: true }); await wrapped({ arg: 'value' }); // Hit await wrapped({ arg: 'value' }); // Hit // Act await wrapped({ arg: 'value' }); // Miss // Assert await wrapped({ arg: 'value' }); // Hit const toolMetrics = metrics.getToolMetrics('CacheTool') as ToolMetrics; await wrapped({ arg: 'value' }); // Hit expect(toolMetrics.cacheHitRate).toBeCloseTo(0.667, 2); // 2/3 hits }); // Assert }); const toolMetrics = metrics.getToolMetrics('CacheTool'); }); expect(toolMetrics.cacheHitRate).toBeCloseTo(0.667, 2); // 2/3 hits }); }); });

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/EricA1019/CTS_MCP'

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