Skip to main content
Glama
pshempel

MCP Time Server Node

by pshempel
error-handling-chain.test.ts7.75 kB
/** * Integration tests for the full error handling chain * * These tests verify that our custom errors are properly mapped to MCP format * by the adapter layer. This is the ONLY place we should test MCP error codes. * * Architecture: * Tool throws → Our Error → Adapter maps → MCP Error → Client receives */ import { executeToolFunction } from '../../src/index'; import { ErrorCode } from '@modelcontextprotocol/sdk/types.js'; describe('Error Handling Chain - Full Integration', () => { describe('ValidationError → InvalidParams', () => { it('should map ValidationError to MCP InvalidParams for missing required fields', async () => { const result = await executeToolFunction('days_until', {}); expect('error' in result).toBe(true); if ('error' in result) { expect(result.error).toMatchObject({ code: ErrorCode.InvalidParams, // -32602 message: expect.stringContaining('target_date is required'), }); } }); it('should map ValidationError to MCP InvalidParams for invalid unit', async () => { const result = await executeToolFunction('add_time', { time: '2024-01-01', amount: 1, unit: 'invalid-unit', }); expect('error' in result).toBe(true); if ('error' in result) { expect(result.error).toMatchObject({ code: ErrorCode.InvalidParams, message: expect.stringContaining('Invalid unit'), }); } }); it('should map ValidationError to MCP InvalidParams for invalid pattern', async () => { const result = await executeToolFunction('next_occurrence', { pattern: 'invalid-pattern', }); expect('error' in result).toBe(true); if ('error' in result) { expect(result.error).toMatchObject({ code: ErrorCode.InvalidParams, message: expect.stringContaining('Invalid pattern'), }); } }); }); describe('TimezoneError → InvalidParams', () => { it('should map TimezoneError to MCP InvalidParams', async () => { const result = await executeToolFunction('get_current_time', { timezone: 'Invalid/Timezone', }); expect('error' in result).toBe(true); if ('error' in result) { expect(result.error).toMatchObject({ code: ErrorCode.InvalidParams, message: expect.stringContaining('Invalid timezone'), data: expect.objectContaining({ timezone: 'Invalid/Timezone', }), }); } }); it('should preserve timezone details in error data', async () => { const result = await executeToolFunction('convert_timezone', { time: '2024-01-01', from_timezone: 'America/New_York', to_timezone: 'Bad/Zone', }); expect('error' in result).toBe(true); if ('error' in result) { expect(result.error).toMatchObject({ code: ErrorCode.InvalidParams, message: expect.stringContaining('Timezone error'), data: expect.objectContaining({ timezone: 'Bad/Zone', }), }); } }); }); describe('DateParsingError → InvalidParams', () => { it('should map DateParsingError to MCP InvalidParams', async () => { const result = await executeToolFunction('add_time', { time: 'not-a-date', amount: 1, unit: 'days', }); expect('error' in result).toBe(true); if ('error' in result) { expect(result.error).toMatchObject({ code: ErrorCode.InvalidParams, message: expect.stringContaining('Date parsing error'), }); } }); it('should handle natural language parsing failures', async () => { const result = await executeToolFunction('days_until', { target_date: 'completely invalid gibberish', }); expect('error' in result).toBe(true); if ('error' in result) { expect(result.error).toMatchObject({ code: ErrorCode.InvalidParams, message: expect.stringContaining('Invalid target_date format'), }); } }); }); describe('BusinessHoursError → InvalidRequest', () => { it('should map errors in business hours calculation', async () => { const result = await executeToolFunction('calculate_business_hours', { start_time: '2024-01-01T09:00:00', end_time: '2024-01-02T17:00:00', business_hours: { start: '25:00', // Invalid time format end: '17:00', }, }); expect('error' in result).toBe(true); if ('error' in result) { // The actual error comes from date parsing, which maps to InternalError // This is correct behavior - the tool catches the error internally expect(result.error.code).toBeDefined(); expect(result.error.message).toBeDefined(); } }); }); describe('Error data preservation', () => { it('should preserve error details through the chain', async () => { const result = await executeToolFunction('get_current_time', { timezone: 'Fake/Zone', }); expect('error' in result).toBe(true); if ('error' in result) { expect(result.error.data).toBeDefined(); expect(result.error.data).toHaveProperty('timezone', 'Fake/Zone'); } }); it('should handle errors without details gracefully', async () => { const result = await executeToolFunction('unknown_tool', {}); expect('error' in result).toBe(true); if ('error' in result) { expect(result.error).toMatchObject({ code: ErrorCode.InternalError, // -32603 message: expect.stringContaining('Unknown tool'), }); } }); }); describe('Edge cases', () => { it('should handle circular references in error objects', async () => { // This would be triggered by internal bugs, hard to test directly // But our mapper handles it, so we verify the chain doesn't break const result = await executeToolFunction('format_time', { time: 'invalid-time', // Will cause an error format: 'relative', }); expect('error' in result).toBe(true); if ('error' in result) { expect(result.error.code).toBeDefined(); expect(result.error.message).toBeDefined(); } }); it('should truncate extremely large error details', async () => { // Try to trigger a large error by passing huge invalid data const hugeString = 'x'.repeat(100000); const result = await executeToolFunction('get_current_time', { timezone: hugeString, }); expect('error' in result).toBe(true); if ('error' in result) { expect(result.error).toMatchObject({ code: ErrorCode.InvalidParams, message: expect.stringContaining('timezone exceeds maximum length'), }); // The data should be truncated if it was too large if (result.error.data) { const serialized = JSON.stringify(result.error.data); expect(serialized.length).toBeLessThan(15000); // Reasonable size } } }); }); describe('Success cases should not be affected', () => { it('should still return successful results normally', async () => { const result = await executeToolFunction('get_server_info', {}); expect('error' in result).toBe(false); if (!('error' in result)) { expect(result).toHaveProperty('content'); const parsed = JSON.parse(result.content[0].text); expect(parsed).toHaveProperty('version'); expect(parsed).toHaveProperty('branch'); expect(parsed).toHaveProperty('timezone'); } }); }); });

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/pshempel/mcp-time-server-node'

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