Skip to main content
Glama
pshempel

MCP Time Server Node

by pshempel
DEVELOPER.md10.9 kB
# Developer Guide - Adding New Tools to MCP Time Server This guide provides step-by-step instructions for adding new tools, with all the details that keep getting looked up repeatedly. ## Table of Contents 1. [Adding a New Tool - Complete Checklist](#adding-a-new-tool---complete-checklist) 2. [Cache TTL Constants](#cache-ttl-constants) 3. [Integration Test Architecture](#integration-test-architecture) 4. [Common Import Patterns](#common-import-patterns) 5. [Error Handling Patterns](#error-handling-patterns) 6. [Testing Patterns](#testing-patterns) ## Adding a New Tool - Complete Checklist When adding a new tool like `calculate_deadline`, follow these steps IN ORDER: ### 1. Research Phase ```bash # Create research script touch research/calculate-deadline-research.js # Test date-fns behavior, edge cases, timezone handling ``` ### 2. Documentation Phase ```bash # Document verified behavior touch docs/verified-behaviors/calculate-deadline-behavior.md ``` ### 3. Test Phase (TDD - Red) ```bash # Create unit test file touch tests/tools/calculateDeadline.test.ts # Create integration test file touch tests/integration/tools/calculateDeadline.integration.test.ts # Run tests to see them fail (RED phase) make test ``` ### 4. Implementation Phase (TDD - Green) ```bash # Create tool implementation touch src/tools/calculateDeadline.ts # Add to exports in src/tools/index.ts # Run tests until they pass make test-watch # Or make test-quick if having issues ``` ### 5. Server Integration - THREE PLACES TO UPDATE #### A. Update src/index.ts ```typescript // 1. Import the tool import { // ... existing imports calculateDeadline, // ADD THIS } from './tools/index.js'; // 2. Add tool definition to TOOL_DEFINITIONS array { name: 'calculate_deadline', description: 'Calculate project deadline considering business days', inputSchema: { type: 'object' as const, properties: { start_date: { type: 'string' as const, description: 'Start date' }, days: { type: 'number' as const, description: 'Number of business days' }, timezone: { type: 'string' as const, description: 'Timezone (default: system)' }, holidays: { type: 'array' as const, items: { type: 'string' as const }, description: 'Holiday dates to exclude' }, }, required: ['start_date', 'days'], }, }, // 3. Add to TOOL_FUNCTIONS mapping const TOOL_FUNCTIONS: Record<string, (params: unknown) => unknown> = { // ... existing mappings calculate_deadline: (params: unknown) => calculateDeadline(params as Parameters<typeof calculateDeadline>[0]), }; ``` #### B. Update tests/integration/helpers/setup.ts (CRITICAL!) ```typescript // 1. Import the tool import { // ... existing imports calculateDeadline, // ADD THIS } from '../../../src/tools/index'; // 2. Add EXACT SAME tool definition to TOOL_DEFINITIONS array // MUST match src/index.ts exactly! // 3. Add to TOOL_FUNCTIONS mapping const TOOL_FUNCTIONS: Record<string, (params: any) => any> = { // ... existing mappings calculate_deadline: calculateDeadline as (params: any) => any, }; ``` #### C. Update test expectations ```typescript // tests/integration/protocol.test.ts // Update tool count from 10 to 11 expect(result.tools).toHaveLength(11); ``` ### 6. Final Steps ```bash # Verify everything works make verify # Update documentation - Update README.md tool count and list - Update scripts/test-mcp-comprehensive.js with test cases # Commit your work git add -A git commit -m "feat: implement calculate_deadline tool" ``` ### 7. If Things Go Wrong ```bash # Jest module resolution issues make fix-jest # Still broken? Nuclear option: make reset ``` ## Cache TTL Constants Located in `src/cache/timeCache.ts`: ```typescript export const CacheTTL = { CURRENT_TIME: 1, // 1 second - for "now" operations TIMEZONE_CONVERT: 300, // 5 minutes - for timezone conversions CALCULATIONS: 3600, // 1 hour - for most calculations BUSINESS_DAYS: 86400, // 24 hours - for business days/holidays }; ``` ### When to use each: - `CURRENT_TIME`: Operations that change every second (current time, "today") - `TIMEZONE_CONVERT`: Timezone conversion results - `CALCULATIONS`: Most date calculations that don't change frequently - `BUSINESS_DAYS`: Holiday lookups and business day calculations ### Cache Implementation Pattern: ```typescript import { CacheTTL, cache } from '../cache/timeCache'; export function myTool(params: MyToolParams): any { const cacheKey = `mytool:${JSON.stringify(params)}`; const cached = cache.get<any>(cacheKey); if (cached !== undefined) { return cached; } // ... do calculation const result = calculateSomething(); // Cache with appropriate TTL cache.set(cacheKey, result, CacheTTL.CALCULATIONS); return result; } ``` ## Integration Test Architecture ### Why Duplicate Tool Definitions? The integration tests maintain **separate tool definitions** in `tests/integration/helpers/setup.ts` to: 1. Test the full MCP protocol flow 2. Isolate test environment from production code 3. Allow testing of protocol-level behaviors ### Key Files: - `src/index.ts` - Production tool definitions - `tests/integration/helpers/setup.ts` - Test tool definitions (MUST BE KEPT IN SYNC!) ### Integration Test Structure: ```typescript // tests/integration/tools/myTool.integration.test.ts import { createTestEnvironment } from '../helpers/setup'; import { callTool } from '../helpers/tools'; describe('my_tool integration', () => { it('should do something', async () => { const { client, cleanup } = await createTestEnvironment(); try { const result = await callTool(client, 'my_tool', { param1: 'value1', }); expect(result).toBe('expected'); } finally { await cleanup(); } }); }); ``` ## Common Import Patterns ### Tool Implementation Imports: ```typescript // src/tools/myTool.ts import { parseISO } from 'date-fns'; import { toZonedTime, formatInTimeZone } from 'date-fns-tz'; import { MyToolParams, TimeServerError, TimeServerErrorCodes } from '../types'; import { validateTimezone, validateDateString } from '../utils/validation'; import { getSystemTimezone, parseTimeInput } from '../utils/dateUtils'; import { CacheTTL, cache } from '../cache/timeCache'; ``` ### Unit Test Imports: ```typescript // tests/tools/myTool.test.ts import { myTool } from '../../src/tools/myTool'; import { addDays, subDays } from 'date-fns'; import { TimeServerErrorCodes } from '../../src/types'; ``` ### Integration Test Imports: ```typescript // tests/integration/tools/myTool.integration.test.ts import { createTestEnvironment } from '../helpers/setup'; import { callTool } from '../helpers/tools'; import { addDays } from 'date-fns'; ``` ## Error Handling Patterns ### Consistent Error Format: ```typescript // Always throw this structure: throw { error: { code: TimeServerErrorCodes.INVALID_PARAMETER, message: 'Descriptive error message', details: { param: value }, // optional }, }; ``` ### Common Error Codes: ```typescript export enum TimeServerErrorCodes { INVALID_PARAMETER = 'INVALID_PARAMETER', INVALID_DATE_FORMAT = 'INVALID_DATE_FORMAT', INVALID_TIMEZONE = 'INVALID_TIMEZONE', INVALID_TIME_UNIT = 'INVALID_TIME_UNIT', INVALID_PATTERN = 'INVALID_PATTERN', BUSINESS_HOURS_ERROR = 'BUSINESS_HOURS_ERROR', } ``` ### Error Testing Pattern: ```typescript // Unit tests try { myTool({ bad: 'params' } as any); } catch (e: any) { expect(e.error.code).toBe(TimeServerErrorCodes.INVALID_PARAMETER); expect(e.error.message).toContain('required'); } // Integration tests await expect( callTool(client, 'my_tool', { bad: 'params' }) ).rejects.toMatchObject({ code: 'INVALID_PARAMETER', message: expect.stringContaining('required'), }); ``` ## Testing Patterns ### Date Testing Helpers: ```typescript // Use date-fns for relative dates const tomorrow = addDays(new Date(), 1); const nextWeek = addDays(new Date(), 7); // Format for date-only parameters const dateString = tomorrow.toISOString().split('T')[0]; // YYYY-MM-DD // Create dates in specific timezone (for holidays) const christmas2025 = new Date(2025, 11, 25); // Month is 0-indexed! ``` ### Timezone Testing: ```typescript // Test with multiple timezones const timezones = ['America/New_York', 'Europe/London', 'Asia/Tokyo']; // Empty string = UTC expect(myTool({ timezone: '' })).toBe('UTC result'); // Undefined = system timezone expect(myTool({ timezone: undefined })).toBe('System tz result'); ``` ### Async Test Pattern: ```typescript it('should handle async operations', async () => { const { client, cleanup } = await createTestEnvironment(); try { const result = await callTool(client, 'tool_name', params); expect(result).toBeDefined(); } finally { await cleanup(); // ALWAYS cleanup! } }); ``` ## Quick Reference Commands ```bash # Run specific test file npm test tests/tools/calculateDeadline.test.ts # Run all tests make test # Run tests with MCP reload reminder make test-verify # Build TypeScript npm run build # Lint and fix npm run lint:fix # Full verification make verify ``` ## Common Pitfalls 1. **Forgetting to update integration test setup** - Tool works in unit tests but fails in integration 2. **Wrong cache TTL** - Using CURRENT_TIME for calculations that should be cached longer 3. **ISO date parsing** - Remember: `new Date('2025-01-01')` is UTC, not local! 4. **Not handling empty string timezone** - Empty string means UTC, not system timezone 5. **Missing tool count update** - Protocol tests fail expecting wrong number of tools ## Quick Lookup - Where Things Live ### Cache: - **Cache instance & TTL constants**: `src/cache/timeCache.ts` - **Import**: `import { CacheTTL, cache } from '../cache/timeCache';` ### Error Handling: - **Error codes & types**: `src/types/index.ts` - **Import**: `import { TimeServerErrorCodes } from '../types';` ### Integration Test Setup: - **DUPLICATE tool definitions**: `tests/integration/helpers/setup.ts` - **Test helpers**: `tests/integration/helpers/tools.ts` - **Import**: `import { createTestEnvironment } from '../helpers/setup';` ### Validation & Utils: - **Timezone validation**: `src/utils/validation.ts` - **Date parsing helpers**: `src/utils/dateUtils.ts` - **System timezone**: `getSystemTimezone()` from `src/utils/dateUtils.ts` ### Adding a Tool Requires Updates To: 1. `src/tools/yourTool.ts` - Implementation 2. `src/tools/index.ts` - Export 3. `src/index.ts` - Tool definition & mapping 4. `tests/integration/helpers/setup.ts` - Duplicate definition & mapping 5. `tests/integration/protocol.test.ts` - Update tool count 6. `README.md` - Update tool count & list 7. `scripts/test-mcp-comprehensive.js` - Add test cases --- Remember: When in doubt, check how existing tools do it. The patterns are consistent throughout the codebase.

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