Skip to main content
Glama

Neo N3 MCP Server

by r3e-network
mcp-comprehensive.test.ts18.3 kB
import { jest } from '@jest/globals'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; import path from 'path'; /** * Comprehensive MCP Server Test Suite * * This test suite validates the Neo N3 MCP server against all MCP protocol requirements * and demonstrates comprehensive functionality testing with the latest protocol features. */ describe('Comprehensive MCP Server Tests', () => { let client: any; let serverPath: string; const TEST_TIMEOUT = 45000; // 45 seconds for complex operations beforeAll(async () => { serverPath = path.join(__dirname, '../dist/index.js'); }, TEST_TIMEOUT); beforeEach(async () => { await startMCPServer(); }, TEST_TIMEOUT); afterEach(async () => { await stopMCPServer(); }, 15000); async function startMCPServer() { client = new Client( { name: 'Comprehensive MCP Test Client', version: '1.0.0' }, { capabilities: { tools: {}, resources: {}, prompts: {} } } ); const transport = new StdioClientTransport({ command: 'node', args: [serverPath], env: { ...process.env, NODE_ENV: 'test' } }); await client.connect(transport); } async function stopMCPServer() { if (client) { await client.close(); client = null; } } describe('🚀 MCP Protocol Fundamentals', () => { test('should establish connection and maintain protocol compliance', async () => { expect(client).toBeDefined(); // Verify client is connected and functional const tools = await client.listTools(); expect(tools).toBeDefined(); expect(Array.isArray(tools.tools)).toBe(true); }); test('should support all required MCP methods', async () => { // Test all core MCP protocol methods const tools = await client.listTools(); const resources = await client.listResources(); expect(tools.tools.length).toBeGreaterThan(0); expect(resources.resources.length).toBeGreaterThan(0); console.log(`✅ Server provides ${tools.tools.length} tools and ${resources.resources.length} resources`); }); test('should handle protocol versioning correctly', async () => { // Connection establishment validates protocol version // If we get here, protocol negotiation succeeded expect(client).toBeDefined(); }); }); describe('🔧 Tool Execution & Validation', () => { test('should list all blockchain tools with proper metadata', async () => { const response = await client.listTools(); expect(response.tools.length).toBeGreaterThanOrEqual(30); // Should have 34+ tools const toolNames = response.tools.map((tool: any) => tool.name); const expectedTools = [ 'get_blockchain_info', 'get_block_count', 'get_block', 'get_transaction', 'get_balance', 'create_wallet', 'import_wallet', 'transfer_assets', 'invoke_contract', 'get_network_mode' ]; expectedTools.forEach(expectedTool => { expect(toolNames).toContain(expectedTool); }); // Validate tool structure response.tools.forEach((tool: any) => { expect(tool.name).toBeDefined(); expect(tool.description).toBeDefined(); expect(typeof tool.name).toBe('string'); expect(typeof tool.description).toBe('string'); }); console.log(`✅ Validated ${response.tools.length} tools with proper structure`); }); test('should execute blockchain info tool with valid data', async () => { const response = await client.callTool({ name: 'get_blockchain_info', arguments: {} }); expect(response).toBeDefined(); expect(response.content).toBeDefined(); expect(Array.isArray(response.content)).toBe(true); const content = response.content[0]; expect(content.type).toBe('text'); expect(content.text).toBeDefined(); const data = JSON.parse(content.text); expect(typeof data.height).toBe('number'); expect(data.height).toBeGreaterThan(0); expect(data.network).toBeDefined(); expect(['mainnet', 'testnet', 'both']).toContain(data.network); expect(Array.isArray(data.validators)).toBe(true); console.log(`✅ Blockchain height: ${data.height}, Network: ${data.network}`); }); test('should handle block count retrieval correctly', async () => { const response = await client.callTool({ name: 'get_block_count', arguments: {} }); const data = JSON.parse(response.content[0].text); expect(typeof data.height).toBe('number'); expect(data.height).toBeGreaterThan(0); expect(data.network).toBeDefined(); console.log(`✅ Block count: ${data.height}`); }); test('should validate address balance retrieval', async () => { const testAddress = 'NZNos2WqTbu5oCgyfss9kUJgBXJqhuYAaj'; const response = await client.callTool({ name: 'get_balance', arguments: { address: testAddress } }); const data = JSON.parse(response.content[0].text); expect(data.address).toBe(testAddress); expect(Array.isArray(data.balance)).toBe(true); console.log(`✅ Balance retrieved for ${testAddress}: ${data.balance.length} assets`); }); test('should create wallets with proper structure', async () => { const response = await client.callTool({ name: 'create_wallet', arguments: { password: 'test-password-2024' } }); const wallet = JSON.parse(response.content[0].text); expect(wallet.address).toBeDefined(); expect(wallet.publicKey).toBeDefined(); expect(wallet.encryptedPrivateKey).toBeDefined(); // Validate Neo N3 address format expect(wallet.address).toMatch(/^N[A-Za-z0-9]{33}$/); console.log(`✅ Created wallet: ${wallet.address}`); }); test('should handle network mode operations', async () => { const response = await client.callTool({ name: 'get_network_mode', arguments: {} }); const data = JSON.parse(response.content[0].text); expect(data.mode).toBeDefined(); expect(Array.isArray(data.availableNetworks)).toBe(true); expect(data.availableNetworks.length).toBeGreaterThan(0); console.log(`✅ Network mode: ${data.mode}, Available: ${data.availableNetworks.join(', ')}`); }); test('should handle contract listing', async () => { const response = await client.callTool({ name: 'list_famous_contracts', arguments: {} }); const data = JSON.parse(response.content[0].text); expect(Array.isArray(data.contracts)).toBe(true); expect(data.contracts.length).toBeGreaterThan(0); expect(data.network).toBeDefined(); console.log(`✅ Listed ${data.contracts.length} famous contracts`); }); }); describe('📚 Resource Management & Access', () => { test('should list all available resources', async () => { const response = await client.listResources(); expect(Array.isArray(response.resources)).toBe(true); expect(response.resources.length).toBeGreaterThan(0); const resourceUris = response.resources.map((resource: any) => resource.uri); const expectedResources = [ 'neo://network/status', 'neo://mainnet/status', 'neo://testnet/status' ]; expectedResources.forEach(expectedResource => { expect(resourceUris).toContain(expectedResource); }); console.log(`✅ Available resources: ${response.resources.length}`); }); test('should read network status resource', async () => { const response = await client.readResource('neo://network/status'); expect(response).toBeDefined(); expect(response.contents).toBeDefined(); expect(Array.isArray(response.contents)).toBe(true); const content = response.contents[0]; expect(content.uri).toBe('neo://network/status'); expect(content.text).toBeDefined(); const data = JSON.parse(content.text); expect(typeof data.height).toBe('number'); expect(data.network).toBeDefined(); console.log(`✅ Network status: height ${data.height}`); }); test('should read mainnet-specific resource', async () => { const response = await client.readResource('neo://mainnet/status'); const content = response.contents[0]; const data = JSON.parse(content.text); expect(data.network).toBe('mainnet'); console.log(`✅ Mainnet status: height ${data.height}`); }); test('should read testnet-specific resource', async () => { const response = await client.readResource('neo://testnet/status'); const content = response.contents[0]; const data = JSON.parse(content.text); expect(data.network).toBe('testnet'); console.log(`✅ Testnet status: height ${data.height}`); }); test('should handle parameterized block resources', async () => { const blockHeight = 1000; const response = await client.readResource(`neo://block/${blockHeight}`); const content = response.contents[0]; const data = JSON.parse(content.text); expect(data.index).toBe(blockHeight); console.log(`✅ Block ${blockHeight} resource accessed successfully`); }); }); describe('🛡️ Error Handling & Edge Cases', () => { test('should handle invalid tool names gracefully', async () => { try { await client.callTool({ name: 'invalid_tool_name', arguments: {} }); fail('Should have thrown an error for invalid tool'); } catch (error: any) { expect(error).toBeDefined(); expect(error.message).toBeDefined(); } }); test('should validate required parameters', async () => { try { await client.callTool({ name: 'get_balance', arguments: {} // Missing required 'address' parameter }); fail('Should have thrown an error for missing required parameter'); } catch (error: any) { expect(error).toBeDefined(); expect(error.message.toLowerCase()).toContain('address'); } }); test('should handle invalid addresses properly', async () => { try { await client.callTool({ name: 'get_balance', arguments: { address: 'invalid_address_format' } }); fail('Should have thrown an error for invalid address'); } catch (error: any) { expect(error).toBeDefined(); expect(error.message.toLowerCase()).toContain('address'); } }); test('should handle invalid resource URIs', async () => { try { await client.readResource('neo://invalid/resource/path'); fail('Should have thrown an error for invalid resource'); } catch (error: any) { expect(error).toBeDefined(); } }); test('should handle network connectivity issues gracefully', async () => { // This tests the error handling when blockchain services are unavailable // The exact behavior depends on the service implementation const response = await client.callTool({ name: 'get_blockchain_info', arguments: {} }); // Should either succeed or fail gracefully with proper error expect(response).toBeDefined(); }); }); describe('⚡ Performance & Reliability', () => { test('should handle concurrent tool executions', async () => { const promises = Array(5).fill(0).map((_, index) => client.callTool({ name: 'get_network_mode', arguments: {} }) ); const responses = await Promise.all(promises); expect(responses).toHaveLength(5); responses.forEach((response, index) => { expect(response).toBeDefined(); expect(response.content[0].type).toBe('text'); const data = JSON.parse(response.content[0].text); expect(data.mode).toBeDefined(); }); console.log(`✅ Handled 5 concurrent requests successfully`); }); test('should maintain performance under sequential load', async () => { const startTime = Date.now(); for (let i = 0; i < 3; i++) { const response = await client.callTool({ name: 'get_network_mode', arguments: {} }); expect(response).toBeDefined(); } const duration = Date.now() - startTime; expect(duration).toBeLessThan(15000); // Should complete within 15 seconds console.log(`✅ Sequential execution completed in ${duration}ms`); }); test('should handle large data responses properly', async () => { const response = await client.callTool({ name: 'get_blockchain_info', arguments: {} }); const content = response.content[0]; expect(content.text.length).toBeGreaterThan(0); // Validate JSON parsing of potentially large response const data = JSON.parse(content.text); expect(data).toBeDefined(); console.log(`✅ Large response handled: ${content.text.length} characters`); }); }); describe('🔄 Workflow Integration Tests', () => { test('should support complete wallet workflow', async () => { // Create wallet const walletResponse = await client.callTool({ name: 'create_wallet', arguments: { password: 'workflow-test-2024' } }); const wallet = JSON.parse(walletResponse.content[0].text); expect(wallet.address).toBeDefined(); // Check wallet balance const balanceResponse = await client.callTool({ name: 'get_balance', arguments: { address: wallet.address } }); const balance = JSON.parse(balanceResponse.content[0].text); expect(balance.address).toBe(wallet.address); // Get network info for context const networkResponse = await client.callTool({ name: 'get_blockchain_info', arguments: {} }); const networkInfo = JSON.parse(networkResponse.content[0].text); expect(networkInfo.height).toBeGreaterThan(0); console.log(`✅ Complete workflow: wallet ${wallet.address} on ${networkInfo.network}`); }); test('should support blockchain exploration workflow', async () => { // Get current blockchain info const infoResponse = await client.callTool({ name: 'get_blockchain_info', arguments: {} }); const info = JSON.parse(infoResponse.content[0].text); // Get specific block const blockHeight = Math.max(1, info.height - 100); // Get a block 100 blocks ago const blockResponse = await client.readResource(`neo://block/${blockHeight}`); const blockData = JSON.parse(blockResponse.contents[0].text); expect(blockData.index).toBe(blockHeight); // Get network status const statusResponse = await client.readResource('neo://network/status'); const statusData = JSON.parse(statusResponse.contents[0].text); expect(statusData.height).toBeGreaterThanOrEqual(blockHeight); console.log(`✅ Blockchain exploration: block ${blockHeight} to current ${info.height}`); }); test('should handle multi-network operations', async () => { // Get network mode const modeResponse = await client.callTool({ name: 'get_network_mode', arguments: {} }); const mode = JSON.parse(modeResponse.content[0].text); // Read mainnet status const mainnetResponse = await client.readResource('neo://mainnet/status'); const mainnetData = JSON.parse(mainnetResponse.contents[0].text); expect(mainnetData.network).toBe('mainnet'); // Read testnet status const testnetResponse = await client.readResource('neo://testnet/status'); const testnetData = JSON.parse(testnetResponse.contents[0].text); expect(testnetData.network).toBe('testnet'); console.log(`✅ Multi-network: Mode ${mode.mode}, Mainnet: ${mainnetData.height}, Testnet: ${testnetData.height}`); }); }); describe('📊 Protocol Compliance Validation', () => { test('should follow MCP response format standards', async () => { const response = await client.callTool({ name: 'get_blockchain_info', arguments: {} }); // Validate MCP response structure expect(response).toBeDefined(); expect(response.content).toBeDefined(); expect(Array.isArray(response.content)).toBe(true); expect(response.content.length).toBeGreaterThan(0); const content = response.content[0]; expect(content.type).toBe('text'); expect(typeof content.text).toBe('string'); // Validate content is proper JSON expect(() => JSON.parse(content.text)).not.toThrow(); console.log(`✅ MCP response format validation passed`); }); test('should handle resource URI format compliance', async () => { const resources = await client.listResources(); resources.resources.forEach((resource: any) => { expect(resource.uri).toBeDefined(); expect(typeof resource.uri).toBe('string'); // Validate URI format expect(() => new URL(resource.uri)).not.toThrow(); // Validate Neo-specific URI scheme expect(resource.uri).toMatch(/^neo:\/\//); }); console.log(`✅ All ${resources.resources.length} resource URIs are compliant`); }); test('should maintain consistent tool metadata', async () => { const tools = await client.listTools(); tools.tools.forEach((tool: any) => { expect(tool.name).toBeDefined(); expect(tool.description).toBeDefined(); expect(typeof tool.name).toBe('string'); expect(typeof tool.description).toBe('string'); expect(tool.name.length).toBeGreaterThan(0); expect(tool.description.length).toBeGreaterThan(0); }); console.log(`✅ All ${tools.tools.length} tools have consistent metadata`); }); }); });

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/r3e-network/neo-n3-mcp'

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