Skip to main content
Glama
portel-dev

NCP - Natural Context Provider

by portel-dev
validate-mcp-url.js7.86 kB
#!/usr/bin/env node /** * Validate MCP URL * * Attempts to connect to a URL using MCP protocol to validate it's a real MCP server. * Then auto-discovers tools, auth requirements, and configuration. * * Usage: * node tests/validate-mcp-url.js <url> * node tests/validate-mcp-url.js https://api.example.com/mcp/sse */ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { AuthDetector } from '../dist/auth/auth-detector.js'; import chalk from 'chalk'; const url = process.argv[2]; if (!url || process.argv.includes('--help')) { console.log(` ${chalk.bold('MCP URL Validator')} Validates if a URL is a real MCP server by attempting MCP protocol connection. ${chalk.cyan('Usage:')} node tests/validate-mcp-url.js <url> ${chalk.cyan('Examples:')} node tests/validate-mcp-url.js https://api.example.com/mcp/sse node tests/validate-mcp-url.js http://localhost:3000/sse ${chalk.cyan('What it does:')} 1. Detects auth requirements (401/403 responses) 2. Attempts MCP protocol handshake (initialize) 3. Lists available tools (if successful) 4. Reports server capabilities 5. Suggests config for import ${chalk.cyan('Output:')} - Validation result (✅ valid MCP or ❌ not MCP) - Auth requirements (bearer, oauth, apiKey, etc.) - Available tools - Server info - Suggested config for batch import CSV `); process.exit(0); } console.log(chalk.bold('\n🔍 Validating MCP URL\n')); console.log(`URL: ${chalk.cyan(url)}\n`); // Step 1: Detect auth requirements console.log(chalk.yellow('Step 1: Detecting authentication requirements...')); const authDetector = new AuthDetector(); let authRequirements; try { authRequirements = await authDetector.detect(url); if (authRequirements.type === 'none') { console.log(chalk.green('✅ No authentication required\n')); } else { console.log(chalk.yellow(`🔐 Requires: ${authRequirements.type}`)); if (authRequirements.detected.wwwAuthenticate) { console.log(chalk.dim(` WWW-Authenticate: ${authRequirements.detected.wwwAuthenticate}`)); } if (authRequirements.detected.oauthEndpoints?.deviceAuthUrl) { console.log(chalk.dim(' OAuth endpoints discovered')); } console.log(chalk.dim('\n ℹ️ Will attempt connection without auth (may fail)\n')); } } catch (error) { console.log(chalk.red(`❌ Auth detection failed: ${error.message}\n`)); authRequirements = { type: 'unknown', required: true, fields: [], detected: {} }; } // Step 2: Attempt MCP protocol connection console.log(chalk.yellow('Step 2: Attempting MCP protocol connection...')); let isValidMCP = false; let serverInfo = null; let capabilities = null; let tools = []; let error = null; try { const transport = new SSEClientTransport(new URL(url)); const client = new Client( { name: 'ncp-validator', version: '1.0.0' }, { capabilities: {} } ); // Connect with timeout const connectPromise = client.connect(transport); const timeoutPromise = new Promise((_, reject) => { setTimeout(() => reject(new Error('Connection timeout (10s)')), 10000); }); await Promise.race([connectPromise, timeoutPromise]); console.log(chalk.green('✅ Connected via MCP protocol\n')); // Get server info serverInfo = client.getServerVersion?.() || { name: 'unknown', version: 'unknown' }; capabilities = client.getServerCapabilities?.() || {}; console.log(chalk.cyan('Server Info:')); console.log(` Name: ${serverInfo.name || 'unknown'}`); console.log(` Version: ${serverInfo.version || 'unknown'}`); if (Object.keys(capabilities).length > 0) { console.log(`\nCapabilities: ${Object.keys(capabilities).join(', ')}`); } // Try to list tools try { const toolsResponse = await client.request( { method: 'tools/list' }, { timeout: 5000 } ); tools = toolsResponse.tools || []; console.log(chalk.green(`\n✅ Found ${tools.length} tools:`)); tools.forEach(tool => { console.log(` • ${chalk.bold(tool.name)}`); if (tool.description) { console.log(chalk.dim(` ${tool.description.substring(0, 60)}${tool.description.length > 60 ? '...' : ''}`)); } }); } catch (toolsError) { console.log(chalk.yellow(`\n⚠️ Could not list tools: ${toolsError.message}`)); } isValidMCP = true; // Close connection await client.close(); } catch (connectionError) { error = connectionError; console.log(chalk.red(`❌ MCP connection failed: ${connectionError.message}`)); // Provide helpful diagnostics if (connectionError.message.includes('401') || connectionError.message.includes('403')) { console.log(chalk.yellow('\n💡 This might be a valid MCP that requires authentication')); console.log(chalk.dim(' The server rejected the connection due to missing credentials')); } else if (connectionError.message.includes('timeout')) { console.log(chalk.yellow('\n💡 The server did not respond in time')); console.log(chalk.dim(' It might be offline or very slow')); } else if (connectionError.message.includes('ECONNREFUSED')) { console.log(chalk.yellow('\n💡 Connection refused')); console.log(chalk.dim(' The server is not running or not accessible')); } else { console.log(chalk.yellow('\n💡 This might not be an MCP server')); console.log(chalk.dim(' Or it uses a different protocol/format')); } } // Step 3: Generate report console.log(chalk.bold('\n━'.repeat(60))); console.log(chalk.bold('\n📊 Validation Report\n')); const status = isValidMCP ? chalk.green('✅ VALID MCP SERVER') : chalk.red('❌ NOT A VALID MCP SERVER'); console.log(status + '\n'); // Summary console.log(chalk.cyan('Summary:')); console.log(` URL: ${url}`); console.log(` Auth Type: ${authRequirements.type}`); console.log(` MCP Protocol: ${isValidMCP ? 'Supported' : 'Not detected'}`); if (serverInfo) { console.log(` Server: ${serverInfo.name} v${serverInfo.version}`); } console.log(` Tools: ${tools.length}`); // Next steps if (isValidMCP && authRequirements.type === 'none') { console.log(chalk.bold('\n✨ Ready to Import!\n')); console.log(chalk.cyan('Add to CSV:')); const serverName = serverInfo?.name?.replace(/[^a-z0-9-]/gi, '-').toLowerCase() || 'unknown-mcp'; const description = serverInfo?.name || 'MCP Server'; console.log(chalk.dim(`${serverName},${url},${description}`)); console.log(chalk.cyan('\nOr import directly via AI:')); console.log(chalk.dim(`Tell AI: "Add MCP from ${url}"`)); } else if (isValidMCP && authRequirements.type !== 'none') { console.log(chalk.bold('\n⚠️ Requires Authentication\n')); console.log(chalk.cyan('Auth Requirements:')); console.log(` Type: ${authRequirements.type}`); authRequirements.fields.forEach(field => { const req = field.required ? '(required)' : '(optional)'; console.log(` • ${field.label} ${req}`); if (field.description) { console.log(chalk.dim(` ${field.description}`)); } }); console.log(chalk.cyan('\nTo import:')); console.log(chalk.dim('1. Prepare credentials in clipboard format')); console.log(chalk.dim('2. Add to CSV with URL')); console.log(chalk.dim('3. Run batch-import-mcps.js')); } else { console.log(chalk.bold('\n❌ Cannot Import\n')); console.log(chalk.yellow('Possible reasons:')); console.log(chalk.dim('• Not an MCP server')); console.log(chalk.dim('• Wrong URL or path')); console.log(chalk.dim('• Server offline')); console.log(chalk.dim('• Incompatible protocol version')); if (error) { console.log(chalk.dim(`• Error: ${error.message}`)); } } console.log(chalk.bold('\n━'.repeat(60))); console.log(''); // Exit code process.exit(isValidMCP ? 0 : 1);

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