tool-registration.test.js•4.76 kB
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { LettaServer } from '../../core/server.js';
import { registerToolHandlers } from '../../tools/index.js';
// Mock dependencies
vi.mock('@modelcontextprotocol/sdk/server/index.js');
vi.mock('axios');
vi.mock('../../core/logger.js');
describe('Tool Registration (LMP-84)', () => {
    let server;
    let registeredHandlers;
    beforeEach(() => {
        // Set required env vars
        process.env.LETTA_BASE_URL = 'https://test.letta.com';
        process.env.LETTA_PASSWORD = 'test-password';
        // Track registered handlers
        registeredHandlers = [];
        // Create server instance
        server = new LettaServer();
        // Mock setRequestHandler to capture registrations
        server.server.setRequestHandler = vi.fn((schema, handler) => {
            registeredHandlers.push({ schema, handler });
        });
    });
    describe('Handler Registration', () => {
        it('should register tool handlers', () => {
            registerToolHandlers(server);
            // Should register exactly 2 handlers
            expect(registeredHandlers).toHaveLength(2);
            expect(server.server.setRequestHandler).toHaveBeenCalledTimes(2);
        });
        it('should register tools/list handler first', () => {
            registerToolHandlers(server);
            // First handler should be list tools
            const firstHandler = registeredHandlers[0];
            expect(firstHandler).toBeDefined();
            expect(firstHandler.handler).toBeTypeOf('function');
        });
        it('should register tools/call handler second', () => {
            registerToolHandlers(server);
            // Second handler should be call tool
            const secondHandler = registeredHandlers[1];
            expect(secondHandler).toBeDefined();
            expect(secondHandler.handler).toBeTypeOf('function');
        });
    });
    describe('List Tools Handler', () => {
        it('should return list of available tools', async () => {
            registerToolHandlers(server);
            // Get the list tools handler (first one registered)
            const listToolsHandler = registeredHandlers[0].handler;
            const response = await listToolsHandler({});
            expect(response).toHaveProperty('tools');
            expect(Array.isArray(response.tools)).toBe(true);
            expect(response.tools.length).toBeGreaterThan(0);
        });
        it('should return tools with correct structure', async () => {
            registerToolHandlers(server);
            const listToolsHandler = registeredHandlers[0].handler;
            const response = await listToolsHandler({});
            // Check first tool structure
            const firstTool = response.tools[0];
            expect(firstTool).toHaveProperty('name');
            expect(firstTool).toHaveProperty('description');
            expect(firstTool).toHaveProperty('inputSchema');
        });
        it('should include expected tool names', async () => {
            registerToolHandlers(server);
            const listToolsHandler = registeredHandlers[0].handler;
            const response = await listToolsHandler({});
            const toolNames = response.tools.map((t) => t.name);
            expect(toolNames).toContain('list_agents');
            expect(toolNames).toContain('prompt_agent');
            expect(toolNames).toContain('list_memory_blocks');
        });
    });
    describe('Call Tool Handler', () => {
        it('should be registered as second handler', () => {
            registerToolHandlers(server);
            // Get the call tool handler (second one registered)
            const callToolHandler = registeredHandlers[1].handler;
            expect(callToolHandler).toBeTypeOf('function');
        });
        it('should throw error for unknown tool', async () => {
            registerToolHandlers(server);
            const callToolHandler = registeredHandlers[1].handler;
            const request = {
                params: {
                    name: 'unknown_tool',
                    arguments: {},
                },
            };
            await expect(callToolHandler(request)).rejects.toThrow();
        });
    });
    describe('Error Handling', () => {
        it('should handle registration on server without MCP server', () => {
            const invalidServer = { server: null };
            expect(() => {
                registerToolHandlers(invalidServer);
            }).toThrow();
        });
        it('should handle registration on undefined server', () => {
            expect(() => {
                registerToolHandlers(undefined);
            }).toThrow();
        });
    });
});