Skip to main content
Glama
logging-tools.test.ts16.9 kB
import { registerLoggingTools } from '../../src/tools/logging-tools'; import { SimplifierClient } from '../../src/client/simplifier-client'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { SimplifierLogListResponse, SimplifierLogPagesResponse } from '../../src/client/types'; // Mock the toolresult wrapper jest.mock('../../src/tools/toolresult', () => ({ wrapToolResult: jest.fn() })); // Mock the SimplifierClient jest.mock('../../src/client/simplifier-client'); describe('Logging Tools', () => { let mockServer: jest.Mocked<McpServer>; let mockClient: jest.Mocked<SimplifierClient>; let mockWrapToolResult: jest.MockedFunction<any>; beforeEach(() => { // Create mock server with tool method mockServer = { tool: jest.fn(), } as any; // Create mock client mockClient = { listLogEntriesPaginated: jest.fn(), getLogPages: jest.fn(), } as any; // Get the mocked wrapToolResult mockWrapToolResult = require('../../src/tools/toolresult').wrapToolResult; mockWrapToolResult.mockClear(); }); afterEach(() => { jest.clearAllMocks(); }); const mockLogListResponse: SimplifierLogListResponse = { list: [ { id: 'LOG001', entryDate: '2025-01-15T10:30:00Z', level: 3, messageKey: 'System_Error', messageParams: [], hasDetails: true, category: 'System' }, { id: 'LOG002', entryDate: '2025-01-15T09:15:00Z', level: 2, messageKey: 'System_Warning', messageParams: ['param1'], hasDetails: false, category: 'Application' }, { id: 'LOG003', entryDate: '2025-01-15T08:00:00Z', level: 1, messageKey: 'System_Info', messageParams: [], hasDetails: false, category: 'System' } ] }; const mockLogPagesResponse: SimplifierLogPagesResponse = { pages: 3, pagesize: 50 }; describe('registerLoggingTools', () => { it('should register the logging-list tool', () => { registerLoggingTools(mockServer, mockClient); expect(mockServer.tool).toHaveBeenCalledTimes(1); expect(mockServer.tool).toHaveBeenCalledWith( 'logging-list', expect.any(String), expect.any(Object), expect.any(Object), expect.any(Function) ); }); it('should register tool with correct schema', () => { registerLoggingTools(mockServer, mockClient); const call = mockServer.tool.mock.calls[0]; const schema = call[2]; expect(schema).toHaveProperty('pageSize'); expect(schema).toHaveProperty('page'); expect(schema).toHaveProperty('logLevel'); expect(schema).toHaveProperty('since'); expect(schema).toHaveProperty('from'); expect(schema).toHaveProperty('until'); }); it('should register tool with correct metadata', () => { registerLoggingTools(mockServer, mockClient); const call = mockServer.tool.mock.calls[0]; const metadata = call[3]; expect(metadata).toEqual({ title: 'List Simplifier Log Entries', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }); }); }); describe('logging-list tool handler', () => { let loggingListHandler: any; beforeEach(() => { registerLoggingTools(mockServer, mockClient); loggingListHandler = mockServer.tool.mock.calls[0][4]; // Handler function }); it('should call wrapToolResult with correct parameters', async () => { mockWrapToolResult.mockResolvedValue({ content: [] }); await loggingListHandler({}); expect(mockWrapToolResult).toHaveBeenCalledWith( 'list log entries', expect.any(Function) ); }); it('should list log entries with default parameters', async () => { mockClient.listLogEntriesPaginated.mockResolvedValue(mockLogListResponse); mockClient.getLogPages.mockResolvedValue(mockLogPagesResponse); mockWrapToolResult.mockImplementation(async (_description: string, fn: () => any) => { const result = await fn(); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); const result = await loggingListHandler({ pageSize: 50, page: 0 }); expect(mockClient.listLogEntriesPaginated).toHaveBeenCalledWith(0, 50, "MCP Tool: logging-list", {}); expect(mockClient.getLogPages).toHaveBeenCalledWith(50, {}); const resultData = JSON.parse(result.content[0].text); expect(resultData.logs).toHaveLength(3); expect(resultData.totalCount).toBe(3); expect(resultData.page).toBe(0); expect(resultData.pageSize).toBe(50); expect(resultData.totalPages).toBe(3); }); it('should list log entries with custom pageSize', async () => { mockClient.listLogEntriesPaginated.mockResolvedValue(mockLogListResponse); mockClient.getLogPages.mockResolvedValue({ pages: 1, pagesize: 10 }); mockWrapToolResult.mockImplementation(async (_description: string, fn: () => any) => { const result = await fn(); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); const result = await loggingListHandler({ pageSize: 10, page: 0 }); expect(mockClient.listLogEntriesPaginated).toHaveBeenCalledWith(0, 10, "MCP Tool: logging-list", {}); expect(mockClient.getLogPages).toHaveBeenCalledWith(10, {}); const resultData = JSON.parse(result.content[0].text); expect(resultData.pageSize).toBe(10); }); it('should list log entries with custom page number', async () => { mockClient.listLogEntriesPaginated.mockResolvedValue(mockLogListResponse); mockClient.getLogPages.mockResolvedValue(mockLogPagesResponse); mockWrapToolResult.mockImplementation(async (_description: string, fn: () => any) => { const result = await fn(); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); const result = await loggingListHandler({ page: 2, pageSize: 50 }); expect(mockClient.listLogEntriesPaginated).toHaveBeenCalledWith(2, 50, "MCP Tool: logging-list", {}); const resultData = JSON.parse(result.content[0].text); expect(resultData.page).toBe(2); }); it('should filter by log level', async () => { mockClient.listLogEntriesPaginated.mockResolvedValue(mockLogListResponse); mockClient.getLogPages.mockResolvedValue(mockLogPagesResponse); mockWrapToolResult.mockImplementation(async (_description: string, fn: () => any) => { const result = await fn(); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); const result = await loggingListHandler({ logLevel: 3, pageSize: 50, page: 0 }); expect(mockClient.listLogEntriesPaginated).toHaveBeenCalledWith(0, 50, "MCP Tool: logging-list", { logLevel: 3 }); expect(mockClient.getLogPages).toHaveBeenCalledWith(50, { logLevel: 3 }); const resultData = JSON.parse(result.content[0].text); expect(resultData.filters.logLevel).toBe(3); }); it('should filter by since timestamp', async () => { mockClient.listLogEntriesPaginated.mockResolvedValue(mockLogListResponse); mockClient.getLogPages.mockResolvedValue(mockLogPagesResponse); mockWrapToolResult.mockImplementation(async (_description: string, fn: () => any) => { const result = await fn(); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); const result = await loggingListHandler({ since: '2025-01-01T00:00:00Z', pageSize: 50, page: 0 }); expect(mockClient.listLogEntriesPaginated).toHaveBeenCalledWith(0, 50, "MCP Tool: logging-list", { since: '2025-01-01T00:00:00Z' }); const resultData = JSON.parse(result.content[0].text); expect(resultData.filters.since).toBe('2025-01-01T00:00:00Z'); }); it('should filter by from and until timestamps', async () => { mockClient.listLogEntriesPaginated.mockResolvedValue(mockLogListResponse); mockClient.getLogPages.mockResolvedValue(mockLogPagesResponse); mockWrapToolResult.mockImplementation(async (_description: string, fn: () => any) => { const result = await fn(); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); const result = await loggingListHandler({ from: '2025-01-01T00:00:00Z', until: '2025-01-31T23:59:59Z', pageSize: 50, page: 0 }); expect(mockClient.listLogEntriesPaginated).toHaveBeenCalledWith(0, 50, "MCP Tool: logging-list", { from: '2025-01-01T00:00:00Z', until: '2025-01-31T23:59:59Z' }); const resultData = JSON.parse(result.content[0].text); expect(resultData.filters.from).toBe('2025-01-01T00:00:00Z'); expect(resultData.filters.until).toBe('2025-01-31T23:59:59Z'); }); it('should combine multiple filters', async () => { mockClient.listLogEntriesPaginated.mockResolvedValue(mockLogListResponse); mockClient.getLogPages.mockResolvedValue(mockLogPagesResponse); mockWrapToolResult.mockImplementation(async (_description: string, fn: () => any) => { const result = await fn(); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); const result = await loggingListHandler({ pageSize: 25, page: 1, logLevel: 2, since: '2025-01-01T00:00:00Z' }); expect(mockClient.listLogEntriesPaginated).toHaveBeenCalledWith(1, 25, "MCP Tool: logging-list", { logLevel: 2, since: '2025-01-01T00:00:00Z' }); expect(mockClient.getLogPages).toHaveBeenCalledWith(25, { logLevel: 2, since: '2025-01-01T00:00:00Z' }); const resultData = JSON.parse(result.content[0].text); expect(resultData.pageSize).toBe(25); expect(resultData.page).toBe(1); expect(resultData.filters.logLevel).toBe(2); expect(resultData.filters.since).toBe('2025-01-01T00:00:00Z'); }); it('should include log entry URIs', async () => { mockClient.listLogEntriesPaginated.mockResolvedValue(mockLogListResponse); mockClient.getLogPages.mockResolvedValue(mockLogPagesResponse); mockWrapToolResult.mockImplementation(async (_description: string, fn: () => any) => { const result = await fn(); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); const result = await loggingListHandler({}); const resultData = JSON.parse(result.content[0].text); expect(resultData.logs[0].uri).toBe('simplifier://logging/entry/LOG001'); expect(resultData.logs[1].uri).toBe('simplifier://logging/entry/LOG002'); expect(resultData.logs[2].uri).toBe('simplifier://logging/entry/LOG003'); }); it('should convert numeric log levels to names', async () => { mockClient.listLogEntriesPaginated.mockResolvedValue(mockLogListResponse); mockClient.getLogPages.mockResolvedValue(mockLogPagesResponse); mockWrapToolResult.mockImplementation(async (_description: string, fn: () => any) => { const result = await fn(); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); const result = await loggingListHandler({}); const resultData = JSON.parse(result.content[0].text); expect(resultData.logs[0].levelName).toBe('Error'); // level 3 expect(resultData.logs[1].levelName).toBe('Warning'); // level 2 expect(resultData.logs[2].levelName).toBe('Info'); // level 1 }); it('should handle all log levels', async () => { const allLevelsResponse: SimplifierLogListResponse = { list: [ { id: '1', entryDate: '2025-01-15T10:00:00Z', level: 0, messageKey: 'debug', messageParams: [], hasDetails: false, category: 'Test' }, { id: '2', entryDate: '2025-01-15T10:00:00Z', level: 1, messageKey: 'info', messageParams: [], hasDetails: false, category: 'Test' }, { id: '3', entryDate: '2025-01-15T10:00:00Z', level: 2, messageKey: 'warning', messageParams: [], hasDetails: false, category: 'Test' }, { id: '4', entryDate: '2025-01-15T10:00:00Z', level: 3, messageKey: 'error', messageParams: [], hasDetails: false, category: 'Test' }, { id: '5', entryDate: '2025-01-15T10:00:00Z', level: 4, messageKey: 'critical', messageParams: [], hasDetails: false, category: 'Test' } ] }; mockClient.listLogEntriesPaginated.mockResolvedValue(allLevelsResponse); mockClient.getLogPages.mockResolvedValue(mockLogPagesResponse); mockWrapToolResult.mockImplementation(async (_description: string, fn: () => any) => { const result = await fn(); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); const result = await loggingListHandler({}); const resultData = JSON.parse(result.content[0].text); expect(resultData.logs[0].levelName).toBe('Debug'); expect(resultData.logs[1].levelName).toBe('Info'); expect(resultData.logs[2].levelName).toBe('Warning'); expect(resultData.logs[3].levelName).toBe('Error'); expect(resultData.logs[4].levelName).toBe('Critical'); }); it('should handle API errors through wrapper', async () => { const testError = new Error('API Connection Failed'); mockClient.listLogEntriesPaginated.mockRejectedValue(testError); mockWrapToolResult.mockImplementation(async (_description: string, fn: () => any) => { try { await fn(); return { content: [] }; } catch (e: any) { return { content: [{ type: 'text', text: JSON.stringify({ error: e.message }) }], isError: true }; } }); const result = await loggingListHandler({}); expect(mockClient.listLogEntriesPaginated).toHaveBeenCalled(); expect(result.content[0].text).toContain('API Connection Failed'); }); it('should handle empty result set', async () => { const emptyResponse: SimplifierLogListResponse = { list: [] }; mockClient.listLogEntriesPaginated.mockResolvedValue(emptyResponse); mockClient.getLogPages.mockResolvedValue({ pages: 0, pagesize: 50 }); mockWrapToolResult.mockImplementation(async (_description: string, fn: () => any) => { const result = await fn(); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); const result = await loggingListHandler({}); const resultData = JSON.parse(result.content[0].text); expect(resultData.logs).toHaveLength(0); expect(resultData.totalCount).toBe(0); expect(resultData.totalPages).toBe(0); }); it('should not include from/until in options when only from is provided', async () => { mockClient.listLogEntriesPaginated.mockResolvedValue(mockLogListResponse); mockClient.getLogPages.mockResolvedValue(mockLogPagesResponse); mockWrapToolResult.mockImplementation(async (_description: string, fn: () => any) => { await fn(); return { content: [] }; }); await loggingListHandler({ from: '2025-01-01T00:00:00Z', pageSize: 50, page: 0 }); expect(mockClient.listLogEntriesPaginated).toHaveBeenCalledWith(0, 50, "MCP Tool: logging-list", {}); }); it('should not include from/until in options when only until is provided', async () => { mockClient.listLogEntriesPaginated.mockResolvedValue(mockLogListResponse); mockClient.getLogPages.mockResolvedValue(mockLogPagesResponse); mockWrapToolResult.mockImplementation(async (_description: string, fn: () => any) => { await fn(); return { content: [] }; }); await loggingListHandler({ until: '2025-01-31T23:59:59Z', pageSize: 50, page: 0 }); expect(mockClient.listLogEntriesPaginated).toHaveBeenCalledWith(0, 50, "MCP Tool: logging-list", {}); }); }); });

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/SimplifierIO/simplifier-mcp'

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