Skip to main content
Glama
portel-dev

NCP - Natural Context Provider

by portel-dev
mcp-wrapper.test.ts8.07 kB
/** * Unit Tests - MCPWrapper * Tests wrapper script generation, log management, and directory handling * Adapted from commercial NCP test patterns */ import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals'; import { MCPWrapper } from '../src/utils/mcp-wrapper.js'; import * as fs from 'fs'; import * as path from 'path'; import { tmpdir } from 'os'; describe('MCPWrapper', () => { let mcpWrapper: MCPWrapper; let testDir: string; beforeEach(() => { jest.clearAllMocks(); // Create a temporary test directory testDir = path.join(tmpdir(), `mcp-wrapper-test-${Date.now()}`); fs.mkdirSync(testDir, { recursive: true }); mcpWrapper = new MCPWrapper(); }); afterEach(() => { // Clean up test directory if (fs.existsSync(testDir)) { fs.rmSync(testDir, { recursive: true, force: true }); } }); describe('initialization', () => { it('should create MCP wrapper successfully', () => { expect(mcpWrapper).toBeDefined(); }); }); describe('wrapper creation', () => { it('should create wrapper script for MCP server', () => { const result = mcpWrapper.createWrapper('test-mcp', 'node', ['script.js']); expect(result).toBeDefined(); expect(result.command).toBeDefined(); expect(Array.isArray(result.args)).toBe(true); }); it('should handle different command formats', () => { const result1 = mcpWrapper.createWrapper('test1', 'node', ['script.js']); const result2 = mcpWrapper.createWrapper('test2', 'python', ['-m', 'module']); expect(result1.command).toBeDefined(); expect(result2.command).toBeDefined(); }); it('should handle commands without arguments', () => { const result = mcpWrapper.createWrapper('test', 'echo'); expect(result).toBeDefined(); expect(result.command).toBeDefined(); }); }); describe('edge cases', () => { it('should handle empty MCP name', () => { const result = mcpWrapper.createWrapper('', 'echo'); expect(result).toBeDefined(); }); it('should handle special characters in MCP name', () => { const result = mcpWrapper.createWrapper('test-mcp_123', 'echo'); expect(result).toBeDefined(); }); }); describe('log file utilities', () => { it('should get log file path for MCP', () => { const logFile = mcpWrapper.getLogFile('test-mcp'); expect(typeof logFile).toBe('string'); expect(logFile).toContain('test-mcp'); }); it('should list all log files', () => { const logFiles = mcpWrapper.listLogFiles(); expect(Array.isArray(logFiles)).toBe(true); }); }); describe('Windows path escaping', () => { it('should properly escape Windows-style paths in wrapper script', () => { // Create wrapper with a command that might contain backslashes on Windows const result = mcpWrapper.createWrapper('test-mcp', 'C:\\Program Files\\nodejs\\node.exe', ['script.js']); expect(result).toBeDefined(); expect(result.command).toBe('node'); // Wrapper uses node to run the script // The wrapper script file should exist and contain properly escaped paths const wrapperPath = result.args[0]; expect(fs.existsSync(wrapperPath)).toBe(true); const wrapperContent = fs.readFileSync(wrapperPath, 'utf-8'); // The command should be JSON-encoded (wrapped in quotes with escaped backslashes) // JSON.stringify('C:\\Program Files\\nodejs\\node.exe') produces: // "C:\\Program Files\\nodejs\\node.exe" (with escaped backslashes) expect(wrapperContent).toContain('const command = '); // Verify the script is valid JavaScript by checking it doesn't have unescaped backslashes // that would cause issues like \n becoming newline, \t becoming tab, etc. // If backslashes were not escaped, 'C:\Program Files\nodejs\node.exe' would have // \n interpreted as newline, corrupting the path expect(wrapperContent).not.toMatch(/const command = 'C:\\Program/); // Not single-quoted unescaped }); it('should handle paths with special escape sequences', () => { // Test paths that would cause issues if not properly escaped: // \n = newline, \t = tab, \r = carriage return, \u = unicode escape const problematicPaths = [ 'C:\\Users\\newuser\\test', // Contains \n and \t patterns 'C:\\Users\\test\\node_modules', // Contains \n 'C:\\temp\\file.txt', // Contains \t 'C:\\ubuntu\\path', // Contains \u ]; for (const testPath of problematicPaths) { const result = mcpWrapper.createWrapper('test-escape', testPath, []); expect(result).toBeDefined(); const wrapperPath = result.args[0]; expect(fs.existsSync(wrapperPath)).toBe(true); const wrapperContent = fs.readFileSync(wrapperPath, 'utf-8'); // The wrapper script should be valid JavaScript // If escaping failed, the script would have actual newlines/tabs in strings expect(wrapperContent).not.toContain('\n--- MCP'); // This is expected in template expect(wrapperContent).toContain('const command = '); // Command should be defined } }); it('should handle log file paths with Windows backslashes', () => { const logFile = mcpWrapper.getLogFile('test-windows'); // On Windows, the log file path will have backslashes // The path should be valid and not corrupted expect(logFile).toBeDefined(); expect(typeof logFile).toBe('string'); // Path should contain .ncp/logs expect(logFile).toMatch(/[/\\]\.ncp[/\\]logs[/\\]/); }); it('should create wrapper script with JSON-escaped paths', () => { const result = mcpWrapper.createWrapper('json-test', 'node', ['test.js']); const wrapperPath = result.args[0]; const wrapperContent = fs.readFileSync(wrapperPath, 'utf-8'); // Check that paths use JSON encoding (the escapeForJS function uses JSON.stringify) // JSON.stringify produces strings like: "value" with properly escaped content expect(wrapperContent).toContain('const logFile = "'); expect(wrapperContent).toContain('const command = "'); expect(wrapperContent).toContain('const args = ['); }); }); describe('wrapper script validity', () => { it('should generate syntactically valid JavaScript', () => { const result = mcpWrapper.createWrapper('syntax-test', 'node', ['--version']); const wrapperPath = result.args[0]; const wrapperContent = fs.readFileSync(wrapperPath, 'utf-8'); // Try to parse the script as JavaScript // This will throw if the syntax is invalid expect(() => { // Basic syntax check - the script should not have obvious issues // We can't fully eval it, but we can check for common problems // Should have all required variable declarations expect(wrapperContent).toContain('const logFile = '); expect(wrapperContent).toContain('const command = '); expect(wrapperContent).toContain('const args = '); expect(wrapperContent).toContain('const { spawn }'); expect(wrapperContent).toContain('const fs = require'); // Should have proper string delimiters (not broken by backslashes) const logFileMatch = wrapperContent.match(/const logFile = ("[^"]*"|'[^']*')/); expect(logFileMatch).toBeTruthy(); const commandMatch = wrapperContent.match(/const command = ("[^"]*"|'[^']*')/); expect(commandMatch).toBeTruthy(); }).not.toThrow(); }); it('should include MCP name in wrapper script comments', () => { const mcpName = 'my-special-mcp'; const result = mcpWrapper.createWrapper(mcpName, 'node', []); const wrapperPath = result.args[0]; const wrapperContent = fs.readFileSync(wrapperPath, 'utf-8'); // The MCP name should appear in the script comments expect(wrapperContent).toContain(`MCP Wrapper for ${mcpName}`); }); }); });

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/portel-dev/ncp'

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