Skip to main content
Glama

Nexus MCP Server

mcp-server.test.ts6.07 kB
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { JSONValidator } from '../../src/utils/json-validator.js'; import { JsonRpcValidator } from '../../src/utils/json-rpc-validator.js'; import { ConfigurationManager } from '../../src/config/manager.js'; describe('MCP Server JSON-RPC Response Validation', () => { beforeEach(() => { // Mock environment variables for testing vi.stubEnv( 'OPENROUTER_API_KEY', 'sk-or-v1-test-key-that-is-long-enough-for-validation-12345' ); vi.stubEnv('NODE_ENV', 'test'); // Reset singleton instance ConfigurationManager.reset(); }); afterEach(() => { vi.restoreAllMocks(); }); describe('list_tools response format', () => { it('should produce valid JSON-RPC 2.0 response for tools list', () => { // Arrange: Create the same response structure that the server creates const toolsResponse = { tools: [ { name: 'search', description: 'Nexus AI-powered search using Perplexity models via OpenRouter. Searches the web for current information and provides comprehensive answers with sources.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'The search query to process (required, 1-2000 characters)', minLength: 1, maxLength: 2000, }, model: { type: 'string', description: 'Perplexity model to use for search', enum: ['perplexity/sonar'], default: 'perplexity/sonar', }, maxTokens: { type: 'number', description: 'Maximum number of tokens in the response (1-4000)', minimum: 1, maximum: 4000, default: 1000, }, temperature: { type: 'number', description: 'Controls randomness in the response (0-2)', minimum: 0, maximum: 2, default: 0.3, }, }, required: ['query'], }, }, ], }; // Act: Process the response through the same validation that the server uses const wrappedResponse = JSONValidator.wrapMCPResponse(toolsResponse); // Assert: The response should be valid JSON-RPC 2.0 expect(wrappedResponse).toBeDefined(); expect(wrappedResponse.jsonrpc).toBe('2.0'); expect('id' in wrappedResponse).toBe(true); // The response should either have 'result' or 'error', not both expect('result' in wrappedResponse !== 'error' in wrappedResponse).toBe( true ); // CRITICAL: The response should contain the tools, not an error // This is the key assertion that will fail and demonstrate the bug expect('result' in wrappedResponse).toBe(true); expect('error' in wrappedResponse).toBe(false); // Validate using our JSON-RPC validator const validationResult = JsonRpcValidator.validateMessage(wrappedResponse); if (!validationResult.valid) { console.error('Validation errors:', validationResult.errors); console.error( 'Actual response:', JSON.stringify(wrappedResponse, null, 2) ); } expect(validationResult.valid).toBe(true); expect(validationResult.errors).toHaveLength(0); // Should contain tools array (this will fail because we get an error response) if ( 'result' in wrappedResponse && wrappedResponse.result && typeof wrappedResponse.result === 'object' ) { const result = wrappedResponse.result as any; expect(result.tools).toBeDefined(); expect(Array.isArray(result.tools)).toBe(true); // Each tool should have required properties result.tools.forEach((tool: any) => { expect(tool.name).toBeDefined(); expect(tool.description).toBeDefined(); expect(tool.inputSchema).toBeDefined(); }); } }); it('should handle empty tools list with valid JSON-RPC response', () => { // Arrange: Empty tools response const emptyToolsResponse = { tools: [] }; // Act const wrappedResponse = JSONValidator.wrapMCPResponse(emptyToolsResponse); // Assert expect(wrappedResponse.jsonrpc).toBe('2.0'); const validationResult = JsonRpcValidator.validateMessage(wrappedResponse); expect(validationResult.valid).toBe(true); if ( 'result' in wrappedResponse && wrappedResponse.result && typeof wrappedResponse.result === 'object' ) { const result = wrappedResponse.result as any; expect(result.tools).toEqual([]); } }); it('should be serializable and parseable without corruption', () => { // Arrange const toolsResponse = { tools: [ { name: 'search', description: 'Test tool', inputSchema: { type: 'object', properties: { query: { type: 'string' } }, required: ['query'], }, }, ], }; // Act const wrappedResponse = JSONValidator.wrapMCPResponse(toolsResponse); // Assert: Should be serializable without errors expect(() => JSON.stringify(wrappedResponse)).not.toThrow(); // Should be parseable back to same structure const serialized = JSON.stringify(wrappedResponse); const parsed = JSON.parse(serialized); expect(parsed).toEqual(wrappedResponse); // Parsed version should still be valid JSON-RPC const reparsedValidation = JsonRpcValidator.validateMessage(parsed); expect(reparsedValidation.valid).toBe(true); }); }); });

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/adawalli/nexus'

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