Skip to main content
Glama

Google Calendar MCP

tool-registration.test.ts8.85 kB
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { ToolRegistry, ToolSchemas } from '../../../tools/registry.js'; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; /** * Tool Registration Tests * * These tests validate that all tools are properly registered with the MCP server * and that their schemas are correctly extracted, especially for complex schemas * that use .refine() methods (like update-event). */ describe('Tool Registration', () => { let mockServer: McpServer; let registeredTools: Array<{ name: string; description: string; inputSchema: any }>; beforeEach(() => { mockServer = new McpServer({ name: 'test', version: '1.0.0' }); registeredTools = []; // Mock the registerTool method to capture registered tools mockServer.registerTool = vi.fn((name: string, definition: any, _handler: any) => { registeredTools.push({ name, description: definition.description, inputSchema: definition.inputSchema }); // Return a mock RegisteredTool return { name, description: definition.description } as any; }); }); it('should register all tools successfully without errors', async () => { // This should not throw any errors await expect( ToolRegistry.registerAll(mockServer, async () => ({ content: [] })) ).resolves.not.toThrow(); }); it('should register the correct number of tools', async () => { await ToolRegistry.registerAll(mockServer, async () => ({ content: [] })); const expectedToolCount = Object.keys(ToolSchemas).length; expect(registeredTools).toHaveLength(expectedToolCount); }); it('should register all expected tool names', async () => { await ToolRegistry.registerAll(mockServer, async () => ({ content: [] })); const expectedTools = Object.keys(ToolSchemas); const registeredToolNames = registeredTools.map(t => t.name); for (const expectedTool of expectedTools) { expect(registeredToolNames).toContain(expectedTool); } }); it('should have valid input schemas for all tools', async () => { await ToolRegistry.registerAll(mockServer, async () => ({ content: [] })); for (const tool of registeredTools) { expect(tool.inputSchema).toBeDefined(); expect(typeof tool.inputSchema).toBe('object'); // The inputSchema should be either a Zod shape object or have been converted properly // For tools with complex schemas, we should still get a valid object if (tool.name === 'update-event') { // This is the key test - update-event should not have an empty schema expect(Object.keys(tool.inputSchema).length).toBeGreaterThan(0); } } }); it('should properly extract schema for update-event tool with .refine() methods', async () => { await ToolRegistry.registerAll(mockServer, async () => ({ content: [] })); const updateEventTool = registeredTools.find(t => t.name === 'update-event'); expect(updateEventTool).toBeDefined(); const schema = updateEventTool!.inputSchema; expect(schema).toBeDefined(); // The key test: schema should not be empty for update-event expect(Object.keys(schema).length).toBeGreaterThan(0); // Check for key update-event specific properties in the Zod shape expect(schema).toHaveProperty('calendarId'); expect(schema).toHaveProperty('eventId'); expect(schema).toHaveProperty('modificationScope'); expect(schema).toHaveProperty('originalStartTime'); expect(schema).toHaveProperty('futureStartDate'); }); it('should compare update-event with create-event to ensure both have proper schemas', async () => { await ToolRegistry.registerAll(mockServer, async () => ({ content: [] })); const createEventTool = registeredTools.find(t => t.name === 'create-event'); const updateEventTool = registeredTools.find(t => t.name === 'update-event'); expect(createEventTool).toBeDefined(); expect(updateEventTool).toBeDefined(); // Both should have similar basic structure const createSchema = createEventTool!.inputSchema; const updateSchema = updateEventTool!.inputSchema; // Both should have non-empty schemas expect(Object.keys(createSchema).length).toBeGreaterThan(0); expect(Object.keys(updateSchema).length).toBeGreaterThan(0); // Both should have calendarId in their Zod shapes expect(createSchema).toHaveProperty('calendarId'); expect(updateSchema).toHaveProperty('calendarId'); // Update should have additional properties that create doesn't need expect(updateSchema).toHaveProperty('eventId'); expect(updateSchema).toHaveProperty('modificationScope'); // Create should have required properties that update makes optional expect(createSchema).toHaveProperty('summary'); expect(createSchema).toHaveProperty('start'); expect(createSchema).toHaveProperty('end'); }); it('should handle all complex schemas with refinements properly', async () => { await ToolRegistry.registerAll(mockServer, async () => ({ content: [] })); // Tools that use .refine() methods const toolsWithRefinements = ['update-event']; for (const toolName of toolsWithRefinements) { const tool = registeredTools.find(t => t.name === toolName); expect(tool).toBeDefined(); const schema = tool!.inputSchema; expect(schema).toBeDefined(); // Should not be empty - this was the original bug expect(Object.keys(schema).length).toBeGreaterThan(0); } }); it('should validate that tools can be retrieved via getToolsWithSchemas()', () => { const tools = ToolRegistry.getToolsWithSchemas(); expect(tools).toBeDefined(); expect(Array.isArray(tools)).toBe(true); expect(tools.length).toBeGreaterThan(0); // Check that update-event is present and has a valid schema const updateEventTool = tools.find(t => t.name === 'update-event'); expect(updateEventTool).toBeDefined(); expect(updateEventTool!.inputSchema).toBeDefined(); // The inputSchema should be a valid JSON Schema object expect(typeof updateEventTool!.inputSchema).toBe('object'); expect((updateEventTool!.inputSchema as any).type).toBe('object'); }); it('should ensure all datetime fields have proper validation', async () => { await ToolRegistry.registerAll(mockServer, async () => ({ content: [] })); const toolsWithDatetime = ['create-event', 'update-event', 'list-events', 'search-events']; for (const toolName of toolsWithDatetime) { const tool = registeredTools.find(t => t.name === toolName); expect(tool).toBeDefined(); const schema = tool!.inputSchema; expect(Object.keys(schema).length).toBeGreaterThan(0); // Just verify the schema exists and is not empty for datetime tools // The actual field validation is tested elsewhere if (toolName === 'update-event') { expect(schema).toHaveProperty('start'); expect(schema).toHaveProperty('end'); } } }); it('should catch schema extraction issues early', async () => { // Test the schema extraction method directly const updateEventSchema = ToolSchemas['update-event']; expect(updateEventSchema).toBeDefined(); // This should not throw an error const extractedShape = (ToolRegistry as any).extractSchemaShape(updateEventSchema); expect(extractedShape).toBeDefined(); expect(typeof extractedShape).toBe('object'); // Should have the expected properties expect(extractedShape).toHaveProperty('calendarId'); expect(extractedShape).toHaveProperty('eventId'); expect(extractedShape).toHaveProperty('modificationScope'); }); }); /** * Schema Extraction Edge Cases * * Tests to ensure the extractSchemaShape method handles various Zod schema types */ describe('Schema Extraction Edge Cases', () => { it('should handle regular ZodObject schemas', () => { const simpleSchema = ToolSchemas['list-calendars']; const extractedShape = (ToolRegistry as any).extractSchemaShape(simpleSchema); expect(extractedShape).toBeDefined(); }); it('should handle ZodEffects (refined) schemas', () => { const refinedSchema = ToolSchemas['update-event']; const extractedShape = (ToolRegistry as any).extractSchemaShape(refinedSchema); expect(extractedShape).toBeDefined(); expect(typeof extractedShape).toBe('object'); }); it('should handle nested schema structures', () => { const complexSchema = ToolSchemas['create-event']; const extractedShape = (ToolRegistry as any).extractSchemaShape(complexSchema); expect(extractedShape).toBeDefined(); expect(typeof extractedShape).toBe('object'); }); });

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/nspady/google-calendar-mcp'

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