Skip to main content
Glama
portel-dev

NCP - Natural Context Provider

by portel-dev
comprehensive-dxt-test.cjs10.3 kB
#!/usr/bin/env node /** * COMPREHENSIVE DXT TEST * * Tests EVERYTHING that matters for production use: * 1. Server initializes correctly with Claude Desktop clientInfo * 2. find() discovers tools via semantic search * 3. run() can execute MCP tools * 4. Server handles multiple sequential requests without crashing * * This simulates the EXACT Claude Desktop workflow using JSON-RPC protocol. */ const { spawn } = require('child_process'); const fs = require('fs'); const path = require('path'); const PROFILE_PATH = path.join(process.cwd(), '.ncp/profiles/all.json'); const TEST_TIMEOUT = 30000; function log(emoji, message, color = '\x1b[0m') { console.log(`${emoji} ${color}${message}\x1b[0m`); } function logSuccess(message) { log('✅', message, '\x1b[32m'); } function logError(message) { log('❌', message, '\x1b[31m'); } function logInfo(message) { log('ℹ️', message, '\x1b[34m'); } function logWarn(message) { log('⚠️', message, '\x1b[33m'); } class ComprehensiveDXTTest { constructor() { this.server = null; this.responses = []; this.responseBuffer = ''; this.requestId = 0; this.stderr = ''; } start() { return new Promise((resolve, reject) => { logInfo('Starting DXT server (dist/index-mcp.js)...'); this.server = spawn('node', [ path.join(process.cwd(), 'dist/index-mcp.js'), '--profile', 'all' ], { stdio: ['pipe', 'pipe', 'pipe'], env: { ...process.env, NCP_MODE: 'extension', NCP_AUTO_IMPORT: 'true', NO_COLOR: 'true', NCP_DEBUG: 'true' } }); this.server.stdout.on('data', (data) => { this.responseBuffer += data.toString(); const lines = this.responseBuffer.split('\n'); lines.slice(0, -1).forEach(line => { if (line.trim()) { try { const response = JSON.parse(line); this.responses.push(response); } catch (e) { // Ignore non-JSON } } }); this.responseBuffer = lines[lines.length - 1]; }); this.server.stderr.on('data', (data) => { this.stderr += data.toString(); }); this.server.on('error', reject); this.server.on('exit', (code, signal) => { if (code !== 0 && code !== null && signal === null) { reject(new Error(`Server crashed with code ${code}. stderr: ${this.stderr}`)); } }); setTimeout(resolve, 300); }); } sendRequest(method, params = {}) { this.requestId++; const request = { jsonrpc: '2.0', id: this.requestId, method, params }; this.server.stdin.write(JSON.stringify(request) + '\n'); return this.requestId; } sendNotification(method, params = {}) { const notification = { jsonrpc: '2.0', method, params }; this.server.stdin.write(JSON.stringify(notification) + '\n'); } async waitForResponse(id, timeoutMs = 5000) { const startTime = Date.now(); while (true) { const response = this.responses.find(r => r.id === id); if (response) return response; if (Date.now() - startTime > timeoutMs) { throw new Error(`Timeout waiting for response ${id}. stderr: ${this.stderr}`); } await new Promise(resolve => setTimeout(resolve, 10)); } } async stop() { if (this.server) { this.server.stdin.end(); await new Promise(resolve => { this.server.once('exit', resolve); setTimeout(() => { if (!this.server.killed) { this.server.kill(); resolve(); } }, 2000); }); } } } // Test 1: Server initializes with Claude Desktop clientInfo async function test1_Initialize() { logInfo('TEST 1: Server initializes with Claude Desktop clientInfo'); const test = new ComprehensiveDXTTest(); await test.start(); const id = test.sendRequest('initialize', { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'claude-desktop', // EXACT name Claude Desktop sends version: '0.14.0' } }); const response = await test.waitForResponse(id, 5000); await test.stop(); if (response.error) { logError(`Initialize failed: ${response.error.message}`); return false; } if (!response.result?.protocolVersion) { logError('Missing protocolVersion in initialize response'); return false; } if (!response.result?.serverInfo?.name) { logError('Missing serverInfo.name'); return false; } logSuccess('Initialize responded correctly'); return true; } // Test 2: find() tool discovers tools via semantic search async function test2_FindTools() { logInfo('TEST 2: find() discovers tools via semantic search'); const test = new ComprehensiveDXTTest(); await test.start(); // Initialize const initId = test.sendRequest('initialize', { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'claude-desktop', version: '0.14.0' } }); await test.waitForResponse(initId); // Wait for indexing await new Promise(resolve => setTimeout(resolve, 3000)); // Search for file-related tools (should always be available) const findId = test.sendRequest('tools/call', { name: 'find', arguments: { description: 'read file contents', limit: 10 } }); const response = await test.waitForResponse(findId, 10000); await test.stop(); if (response.error) { logError(`find() failed: ${response.error.message}`); return false; } const content = response.result?.content?.[0]?.text || ''; // Check if results contain tool information const hasResults = content.includes('Tool:') || content.includes('match') || content.includes('read') || content.includes('file'); if (hasResults) { logSuccess('find() returned relevant tools'); logInfo(` Results preview: \n${content.substring(0, 300)}...`); return true; } else { logError('find() did not return expected results'); logInfo(` Received: ${content.substring(0, 200)}`); return false; } } // Test 3: run() tool can execute MCP tools async function test3_RunMCPTool() { logInfo('TEST 3: run() can execute MCP tools'); const test = new ComprehensiveDXTTest(); await test.start(); // Initialize const initId = test.sendRequest('initialize', { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'claude-desktop', version: '0.14.0' } }); await test.waitForResponse(initId); // Try to call a safe built-in tool (mcp:list) const runId = test.sendRequest('tools/call', { name: 'run', arguments: { tool: 'mcp:list', parameters: {} } }); const response = await test.waitForResponse(runId, 10000); await test.stop(); if (response.error) { logError(`run() failed: ${response.error.message}`); return false; } const content = response.result?.content?.[0]?.text || ''; if (content.includes('MCP') || content.includes('server')) { logSuccess('run() successfully executed MCP tool'); return true; } else { logError('run() returned unexpected result'); return false; } } // Test 4: Server handles multiple requests without crashing async function test4_MultipleRequests() { logInfo('TEST 4: Server handles 10 sequential requests'); const test = new ComprehensiveDXTTest(); await test.start(); // Initialize const initId = test.sendRequest('initialize', { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'claude-desktop', version: '0.14.0' } }); await test.waitForResponse(initId); // Send 10 tools/list requests for (let i = 0; i < 10; i++) { const id = test.sendRequest('tools/list'); const response = await test.waitForResponse(id, 3000); if (response.error) { await test.stop(); logError(`Request ${i + 1}/10 failed`); return false; } } await test.stop(); logSuccess('Server handled 10 sequential requests successfully'); return true; } // Main test runner async function runAllTests() { console.log('\n' + '='.repeat(70)); console.log('🔬 COMPREHENSIVE DXT TEST SUITE'); console.log(' Testing EVERYTHING that matters for production'); console.log('='.repeat(70) + '\n'); const tests = [ { name: 'Initialize with clientInfo', fn: test1_Initialize }, { name: 'find() discovers tools', fn: test2_FindTools }, { name: 'run() executes tools', fn: test3_RunMCPTool }, { name: 'Multiple requests', fn: test4_MultipleRequests } ]; let passed = 0; let failed = 0; const results = []; for (const test of tests) { try { const result = await test.fn(); if (result) { passed++; results.push({ name: test.name, passed: true }); } else { failed++; results.push({ name: test.name, passed: false }); } } catch (error) { logError(`${test.name} threw error: ${error.message}`); failed++; results.push({ name: test.name, passed: false, error: error.message }); } console.log(''); } console.log('='.repeat(70)); console.log('📊 FINAL RESULTS'); console.log('='.repeat(70)); console.log(`✅ Passed: ${passed}`); console.log(`❌ Failed: ${failed}`); console.log(''); results.forEach(result => { const status = result.passed ? '✅' : '❌'; console.log(`${status} ${result.name}`); if (result.error) { console.log(` Error: ${result.error}`); } }); console.log('='.repeat(70)); if (failed > 0) { console.log('\n❌ COMPREHENSIVE TEST FAILED - DO NOT RELEASE\n'); process.exit(1); } else { console.log('\n✅ ALL COMPREHENSIVE TESTS PASSED - Safe to release\n'); process.exit(0); } } // Run tests with timeout const timeout = setTimeout(() => { logError('Test suite timeout (30s)'); process.exit(1); }, TEST_TIMEOUT); runAllTests().catch(error => { clearTimeout(timeout); logError(`Test suite crashed: ${error.message}`); console.error(error); process.exit(1); }).finally(() => { clearTimeout(timeout); });

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