UNPKG

combined-memory-mcp

Version:

MCP server for Combined Memory API - AI-powered chat with unlimited context, memory management, voice agents, and 500+ tool integrations

192 lines (191 loc) 9.93 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js"); const promises_1 = __importDefault(require("fs/promises")); const path_1 = __importDefault(require("path")); const test_config_1 = require("../fixtures/test-config"); const mcpMapper_1 = require("../../src/mcpMapper"); const testTypes_1 = require("../utils/testTypes"); // Import the mocked API client instead of the real one const apiClient_mock_1 = require("../mocks/apiClient.mock"); // Store original environment variables const originalEnv = Object.assign({}, process.env); // Mock the API client module jest.mock('../../src/apiClient', () => ({ executeApiCall: apiClient_mock_1.executeApiCall })); describe('OpenAPI to MCP Integration Tests', () => { let openApiSpec; beforeAll(() => __awaiter(void 0, void 0, void 0, function* () { // Load the test OpenAPI spec directly from the fixture file const content = yield promises_1.default.readFile(path_1.default.resolve(process.cwd(), test_config_1.testConfig.openApiFile), 'utf-8'); openApiSpec = JSON.parse(content); })); beforeEach(() => { // Reset mocks between tests jest.clearAllMocks(); (0, apiClient_mock_1.resetApiMock)(); // Reset environment variables to original state process.env = Object.assign({}, originalEnv); }); afterAll(() => { // Restore environment process.env = originalEnv; }); describe('OpenAPI Parsing', () => { it('should contain expected paths and operations', () => { // Test the OpenAPI spec structure directly expect(openApiSpec).toBeDefined(); expect(openApiSpec.openapi).toBe('3.0.0'); expect(openApiSpec.info.title).toBe('Petstore API'); // Verify paths expect(openApiSpec.paths).toBeDefined(); expect(openApiSpec.paths["/pets"]).toBeDefined(); expect(openApiSpec.paths["/pets/{petId}"]).toBeDefined(); // Verify operations expect(openApiSpec.paths["/pets"].get).toBeDefined(); expect(openApiSpec.paths["/pets"].get.operationId).toBe('listPets'); expect(openApiSpec.paths["/pets"].post).toBeDefined(); expect(openApiSpec.paths["/pets"].post.operationId).toBe('createPet'); expect(openApiSpec.paths["/pets/{petId}"].get).toBeDefined(); expect(openApiSpec.paths["/pets/{petId}"].get.operationId).toBe('getPetById'); // Verify schemas expect(openApiSpec.components.schemas.Pet).toBeDefined(); expect(openApiSpec.components.schemas.NewPet).toBeDefined(); expect(openApiSpec.components.schemas.Pets).toBeDefined(); }); }); describe('MCP Mapper', () => { it('should map OpenAPI spec to MCP tools', () => { // Test the mapping function const mappedTools = (0, mcpMapper_1.mapOpenApiToMcpTools)(openApiSpec); // Verify the tools were mapped correctly expect(mappedTools).toBeDefined(); expect(Array.isArray(mappedTools)).toBe(true); expect(mappedTools.length).toBe(3); // 3 operations in our test spec // Check tool names const toolNames = mappedTools.map(tool => tool.mcpToolDefinition.name); expect(toolNames).toContain('listPets'); expect(toolNames).toContain('getPetById'); expect(toolNames).toContain('createPet'); // Verify tool structure for one tool const listPetsTool = mappedTools.find(tool => tool.mcpToolDefinition.name === 'listPets'); expect(listPetsTool).toBeDefined(); expect(listPetsTool === null || listPetsTool === void 0 ? void 0 : listPetsTool.mcpToolDefinition.description).toBeDefined(); expect(listPetsTool === null || listPetsTool === void 0 ? void 0 : listPetsTool.mcpToolDefinition.inputSchema).toBeDefined(); expect(listPetsTool === null || listPetsTool === void 0 ? void 0 : listPetsTool.apiCallDetails.method).toBe('GET'); }); it('should respect operation filtering via environment variables', () => { // Set the correct environment variables that the config actually uses process.env.MCP_WHITELIST_OPERATIONS = 'listPets'; process.env.MCP_BLACKLIST_OPERATIONS = ''; // Re-require the config to force it to read the updated environment variables jest.resetModules(); jest.doMock('../../src/config', () => ({ config: { filter: { whitelist: ['listPets'], blacklist: [] } } })); // Get a fresh instance of the function with the mocked config const { mapOpenApiToMcpTools } = require('../../src/mcpMapper'); // Test filtering const filteredTools = mapOpenApiToMcpTools(openApiSpec); // Should now only have listPets expect(filteredTools.length).toBe(1); expect(filteredTools[0].mcpToolDefinition.name).toBe('listPets'); // Reset mocks to not affect other tests jest.dontMock('../../src/config'); }); }); describe('API Client', () => { it('should correctly execute API calls', () => __awaiter(void 0, void 0, void 0, function* () { // Set up test data const mockResponse = { id: 1, name: 'Rex', tag: 'dog' }; apiClient_mock_1.executeApiCall.mockImplementationOnce(() => __awaiter(void 0, void 0, void 0, function* () { return ({ success: true, statusCode: 200, data: mockResponse, headers: new Headers({ 'Content-Type': 'application/json' }) }); })); // Create API call details const apiCallDetails = (0, testTypes_1.createTestApiCallDetails)({ method: 'GET', pathTemplate: '/pets/{petId}', serverUrl: 'http://example.com/api' }); // Execute the API call const result = yield (0, apiClient_mock_1.executeApiCall)(apiCallDetails, { petId: '1' }); // Verify the result expect(result).toBeDefined(); expect(result.success).toBe(true); expect(result.statusCode).toBe(200); expect(result.data).toEqual(mockResponse); // Check that executeApiCall was called with the right arguments expect(apiClient_mock_1.executeApiCall).toHaveBeenCalledTimes(1); expect(apiClient_mock_1.executeApiCall).toHaveBeenCalledWith(apiCallDetails, { petId: '1' }); })); it('should handle error responses', () => __awaiter(void 0, void 0, void 0, function* () { // Set up error response const errorResponse = { error: 'Not found' }; (0, apiClient_mock_1.mockApiError)(404, errorResponse); // Create API call details const apiCallDetails = (0, testTypes_1.createTestApiCallDetails)({ method: 'GET', pathTemplate: '/pets/{petId}', serverUrl: 'http://example.com/api' }); // Execute the API call const result = yield (0, apiClient_mock_1.executeApiCall)(apiCallDetails, { petId: '999' }); // Verify the result expect(result).toBeDefined(); expect(result.statusCode).toBe(404); expect(result.success).toBe(false); expect(result.data).toEqual(errorResponse); })); }); describe('MCP Server Integration', () => { it('should create an MCP server with tools', () => { // Create an MCP server const server = new mcp_js_1.McpServer({ name: 'Test Server', version: '1.0.0' }); // Get mapped tools const mappedTools = (0, mcpMapper_1.mapOpenApiToMcpTools)(openApiSpec); // Register tools with server for (const tool of mappedTools) { const { mcpToolDefinition } = tool; // Register a simple handler that just returns a test response server.tool(mcpToolDefinition.name, mcpToolDefinition.description, () => __awaiter(void 0, void 0, void 0, function* () { return ({ content: [{ type: 'text', text: JSON.stringify({ success: true }) }] }); })); } // Verify server has tools registered // Since we can't access server.tools directly, we'll rely on the fact that // registration succeeded without errors as a basic test expect(server).toBeDefined(); }); }); });