Skip to main content
Glama

Letta MCP Server

by oculairmedia
server-initialization.test.js11.9 kB
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { LettaServer } from '../../core/server.js'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import axios from 'axios'; import { createLogger } from '../../core/logger.js'; // Mock dependencies vi.mock('@modelcontextprotocol/sdk/server/index.js'); vi.mock('axios'); vi.mock('../../core/logger.js'); describe('LettaServer Initialization (LMP-82)', () => { let originalEnv; let mockLogger; let mockMCPServer; let mockAxiosInstance; beforeEach(() => { // Save original env vars originalEnv = { LETTA_BASE_URL: process.env.LETTA_BASE_URL, LETTA_PASSWORD: process.env.LETTA_PASSWORD, }; // Set up mock logger mockLogger = { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), child: vi.fn().mockReturnThis(), }; createLogger.mockReturnValue(mockLogger); // Set up mock MCP server mockMCPServer = { setRequestHandler: vi.fn(), onerror: null, connect: vi.fn(), close: vi.fn(), }; Server.mockImplementation(() => mockMCPServer); // Set up mock axios instance mockAxiosInstance = { get: vi.fn(), post: vi.fn(), put: vi.fn(), patch: vi.fn(), delete: vi.fn(), request: vi.fn(), }; axios.create.mockReturnValue(mockAxiosInstance); }); afterEach(() => { // Restore original env vars process.env.LETTA_BASE_URL = originalEnv.LETTA_BASE_URL; process.env.LETTA_PASSWORD = originalEnv.LETTA_PASSWORD; // Clear all mocks vi.clearAllMocks(); }); describe('Environment Variable Validation', () => { it('should throw error when LETTA_BASE_URL is not set', () => { delete process.env.LETTA_BASE_URL; process.env.LETTA_PASSWORD = 'test-password'; expect(() => { new LettaServer(); }).toThrow('Missing required environment variable: LETTA_BASE_URL'); }); it('should throw error when LETTA_BASE_URL is empty string', () => { process.env.LETTA_BASE_URL = ''; process.env.LETTA_PASSWORD = 'test-password'; expect(() => { new LettaServer(); }).toThrow('Missing required environment variable: LETTA_BASE_URL'); }); it('should not throw error when LETTA_PASSWORD is not set', () => { process.env.LETTA_BASE_URL = 'https://test.letta.com'; delete process.env.LETTA_PASSWORD; expect(() => { new LettaServer(); }).not.toThrow(); }); it('should use empty string for password when not set', () => { process.env.LETTA_BASE_URL = 'https://test.letta.com'; delete process.env.LETTA_PASSWORD; const server = new LettaServer(); expect(server.password).toBe(''); }); }); describe('MCP Server Initialization', () => { beforeEach(() => { process.env.LETTA_BASE_URL = 'https://test.letta.com'; process.env.LETTA_PASSWORD = 'test-password'; }); it('should create MCP server with correct configuration', () => { new LettaServer(); expect(Server).toHaveBeenCalledWith( { name: 'letta-server', version: '0.1.0', }, { capabilities: { tools: { listChanged: true, }, prompts: { listChanged: true, }, resources: { subscribe: true, listChanged: true, }, }, }, ); }); it('should set error handler on MCP server', () => { new LettaServer(); const testError = new Error('Test error'); // Trigger the error handler mockMCPServer.onerror(testError); expect(mockLogger.error).toHaveBeenCalledWith('MCP Error', { error: testError }); }); it('should store reference to MCP server instance', () => { const server = new LettaServer(); expect(server.server).toBe(mockMCPServer); }); }); describe('Logger Initialization', () => { beforeEach(() => { process.env.LETTA_BASE_URL = 'https://test.letta.com'; process.env.LETTA_PASSWORD = 'test-password'; }); it('should create logger with correct module name', () => { new LettaServer(); expect(createLogger).toHaveBeenCalledWith('LettaServer'); }); it('should store logger reference', () => { const server = new LettaServer(); expect(server.logger).toBe(mockLogger); }); }); describe('API Client Configuration', () => { it('should append /v1 to base URL if not present', () => { process.env.LETTA_BASE_URL = 'https://test.letta.com'; process.env.LETTA_PASSWORD = 'test-password'; const server = new LettaServer(); expect(axios.create).toHaveBeenCalledWith({ baseURL: 'https://test.letta.com/v1', headers: { 'Content-Type': 'application/json', Accept: 'application/json', }, }); expect(server.apiBase).toBe('https://test.letta.com/v1'); }); it('should not duplicate /v1 if already present', () => { process.env.LETTA_BASE_URL = 'https://test.letta.com/v1'; process.env.LETTA_PASSWORD = 'test-password'; new LettaServer(); expect(axios.create).toHaveBeenCalledWith({ baseURL: 'https://test.letta.com/v1/v1', headers: { 'Content-Type': 'application/json', Accept: 'application/json', }, }); }); it('should create axios instance with correct default headers', () => { process.env.LETTA_BASE_URL = 'https://test.letta.com'; process.env.LETTA_PASSWORD = 'test-password'; new LettaServer(); expect(axios.create).toHaveBeenCalledWith( expect.objectContaining({ headers: { 'Content-Type': 'application/json', Accept: 'application/json', }, }), ); }); it('should store axios instance reference', () => { process.env.LETTA_BASE_URL = 'https://test.letta.com'; process.env.LETTA_PASSWORD = 'test-password'; const server = new LettaServer(); expect(server.api).toBe(mockAxiosInstance); }); }); describe('Property Initialization', () => { beforeEach(() => { process.env.LETTA_BASE_URL = 'https://test.letta.com'; process.env.LETTA_PASSWORD = 'test-password'; }); it('should initialize all required properties', () => { const server = new LettaServer(); expect(server).toHaveProperty('logger'); expect(server).toHaveProperty('server'); expect(server).toHaveProperty('apiBase'); expect(server).toHaveProperty('password'); expect(server).toHaveProperty('api'); }); it('should store password from environment', () => { const server = new LettaServer(); expect(server.password).toBe('test-password'); }); it('should handle special characters in password', () => { process.env.LETTA_PASSWORD = 'p@$$w0rd!#$%^&*()'; const server = new LettaServer(); expect(server.password).toBe('p@$$w0rd!#$%^&*()'); }); }); describe('Edge Cases', () => { it('should handle malformed URLs gracefully', () => { process.env.LETTA_BASE_URL = 'not-a-valid-url'; process.env.LETTA_PASSWORD = 'test-password'; // Should not throw during construction expect(() => { new LettaServer(); }).not.toThrow(); // axios.create should still be called with the malformed URL expect(axios.create).toHaveBeenCalledWith( expect.objectContaining({ baseURL: 'not-a-valid-url/v1', }), ); }); it('should handle very long passwords', () => { const longPassword = 'a'.repeat(1000); process.env.LETTA_BASE_URL = 'https://test.letta.com'; process.env.LETTA_PASSWORD = longPassword; const server = new LettaServer(); expect(server.password).toBe(longPassword); }); it('should handle URLs with trailing slashes', () => { process.env.LETTA_BASE_URL = 'https://test.letta.com/'; process.env.LETTA_PASSWORD = 'test-password'; const server = new LettaServer(); expect(server.apiBase).toBe('https://test.letta.com//v1'); }); }); describe('Multiple Instance Creation', () => { beforeEach(() => { process.env.LETTA_BASE_URL = 'https://test.letta.com'; process.env.LETTA_PASSWORD = 'test-password'; }); it('should allow creating multiple server instances', () => { // Reset mocks to return different instances const mockMCPServer2 = { setRequestHandler: vi.fn(), onerror: null, connect: vi.fn(), close: vi.fn(), }; const mockAxiosInstance2 = { get: vi.fn(), post: vi.fn(), put: vi.fn(), patch: vi.fn(), delete: vi.fn(), request: vi.fn(), }; const mockLogger2 = { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), child: vi.fn().mockReturnThis(), }; Server.mockImplementationOnce(() => mockMCPServer); Server.mockImplementationOnce(() => mockMCPServer2); axios.create.mockReturnValueOnce(mockAxiosInstance); axios.create.mockReturnValueOnce(mockAxiosInstance2); createLogger.mockReturnValueOnce(mockLogger); createLogger.mockReturnValueOnce(mockLogger2); const server1 = new LettaServer(); const server2 = new LettaServer(); expect(server1).not.toBe(server2); expect(server1.server).toBe(mockMCPServer); expect(server2.server).toBe(mockMCPServer2); expect(server1.api).toBe(mockAxiosInstance); expect(server2.api).toBe(mockAxiosInstance2); expect(server1.logger).toBe(mockLogger); expect(server2.logger).toBe(mockLogger2); }); it('should create separate MCP server instances', () => { new LettaServer(); new LettaServer(); expect(Server).toHaveBeenCalledTimes(2); }); it('should create separate axios instances', () => { new LettaServer(); new LettaServer(); expect(axios.create).toHaveBeenCalledTimes(2); }); }); });

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/oculairmedia/Letta-MCP-server'

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