Skip to main content
Glama
test-bearer-auth-comprehensive.cjs11.9 kB
#!/usr/bin/env node /** * Comprehensive test for OAuth Bearer authentication and MCP tool execution * Tests the complete flow: OAuth -> Bearer Token -> MCP Initialize -> Tool Call */ const axios = require('axios'); const crypto = require('crypto'); const readline = require('readline'); // Test configuration const TEST_CONFIG = { tunnelUrl: process.env.TUNNEL_URL || 'https://folding-fold-progressive-intranet.trycloudflare.com', credentials: { username: 'david+saola@umbrellacost.com', password: 'Pileus2023' } }; // PKCE helpers function generateCodeVerifier() { return crypto.randomBytes(32).toString('base64url'); } function generateCodeChallenge(verifier) { return crypto.createHash('sha256').update(verifier).digest('base64url'); } // Test logger class TestLogger { constructor() { this.results = []; } log(message, data = null) { const entry = { timestamp: new Date().toISOString(), message, data }; this.results.push(entry); console.log(`[${entry.timestamp}] ${message}`); if (data) { console.log(JSON.stringify(data, null, 2)); } } error(message, error) { const entry = { timestamp: new Date().toISOString(), message, error: error.message || error }; this.results.push(entry); console.error(`[ERROR] ${message}:`, error.message || error); if (error.response?.data) { console.error('Response data:', JSON.stringify(error.response.data, null, 2)); } } summary() { console.log('\n' + '='.repeat(80)); console.log('TEST SUMMARY'); console.log('='.repeat(80)); const passed = this.results.filter(r => !r.error).length; const failed = this.results.filter(r => r.error).length; console.log(`✅ Passed: ${passed}`); console.log(`❌ Failed: ${failed}`); console.log('='.repeat(80)); } } const logger = new TestLogger(); // Main test suite async function runComprehensiveTest() { logger.log('Starting comprehensive OAuth + Bearer + MCP test suite'); try { // Step 1: Test OAuth endpoints discovery logger.log('\n📋 Step 1: Testing OAuth endpoint discovery'); const authServerUrl = `${TEST_CONFIG.tunnelUrl}/.well-known/oauth-authorization-server`; const authServerResponse = await axios.get(authServerUrl); logger.log('OAuth authorization server metadata retrieved', { issuer: authServerResponse.data.issuer, endpoints: { authorization: authServerResponse.data.authorization_endpoint, token: authServerResponse.data.token_endpoint, registration: authServerResponse.data.registration_endpoint } }); // Step 2: Register client logger.log('\n📋 Step 2: Registering OAuth client'); const registerResponse = await axios.post( `${TEST_CONFIG.tunnelUrl}/register`, { client_name: 'MCP Test Client', grant_types: ['authorization_code'], response_types: ['code'], token_endpoint_auth_method: 'none', scope: 'openid profile email', redirect_uris: ['https://claude.ai/api/mcp/auth_callback'] } ); const clientId = registerResponse.data.client_id; logger.log('Client registered successfully', { clientId }); // Step 3: Get authorization code (simulated) logger.log('\n📋 Step 3: Simulating authorization flow'); const codeVerifier = generateCodeVerifier(); const codeChallenge = generateCodeChallenge(codeVerifier); const state = crypto.randomBytes(16).toString('base64url'); // First, get the login page const authParams = new URLSearchParams({ response_type: 'code', client_id: clientId, redirect_uri: 'https://claude.ai/api/mcp/auth_callback', scope: 'openid profile email', state: state, code_challenge: codeChallenge, code_challenge_method: 'S256' }); const authUrl = `${TEST_CONFIG.tunnelUrl}/authorize?${authParams}`; logger.log('Authorization URL constructed', { url: authUrl }); // Simulate login to get session const loginResponse = await axios.post( `${TEST_CONFIG.tunnelUrl}/login`, new URLSearchParams({ username: TEST_CONFIG.credentials.username, password: TEST_CONFIG.credentials.password, client_id: clientId, state: state }), { maxRedirects: 0, validateStatus: status => status === 302, headers: { 'Content-Type': 'application/x-www-form-urlencoded' } } ); const cookies = loginResponse.headers['set-cookie']; const sessionCookie = cookies?.find(c => c.startsWith('sid=')); logger.log('Login successful, session created'); // Get authorization code using session const authCodeResponse = await axios.get(authUrl, { headers: { Cookie: sessionCookie, Accept: 'text/html' }, maxRedirects: 0, validateStatus: status => status === 200 }); // Parse the authorization code from the response const codeMatch = authCodeResponse.data.match(/code=([^&"'\s]+)/); const authCode = codeMatch ? codeMatch[1] : 'test-code-' + Date.now(); logger.log('Authorization code obtained', { code: authCode.substring(0, 20) + '...' }); // Step 4: Exchange code for token logger.log('\n📋 Step 4: Exchanging authorization code for access token'); const tokenResponse = await axios.post( `${TEST_CONFIG.tunnelUrl}/oauth/token`, new URLSearchParams({ grant_type: 'authorization_code', code: authCode, client_id: clientId, redirect_uri: 'https://claude.ai/api/mcp/auth_callback', code_verifier: codeVerifier }), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } } ); const accessToken = tokenResponse.data.access_token; logger.log('Access token obtained successfully', { tokenType: tokenResponse.data.token_type, expiresIn: tokenResponse.data.expires_in, tokenPreview: accessToken.substring(0, 50) + '...' }); // Step 5: Test MCP endpoint with Bearer token logger.log('\n📋 Step 5: Testing MCP endpoint with Bearer token'); // 5a. Initialize MCP session logger.log('5a. Initializing MCP session'); const initResponse = await axios.post( `${TEST_CONFIG.tunnelUrl}/mcp`, { jsonrpc: '2.0', method: 'initialize', params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'test-client', version: '1.0.0' } }, id: 1 }, { headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' } } ); logger.log('MCP initialized successfully', { protocolVersion: initResponse.data.result?.protocolVersion, serverName: initResponse.data.result?.serverInfo?.name }); // 5b. List available tools logger.log('\n5b. Listing available MCP tools'); const toolsResponse = await axios.post( `${TEST_CONFIG.tunnelUrl}/mcp`, { jsonrpc: '2.0', method: 'tools/list', params: {}, id: 2 }, { headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' } } ); const tools = toolsResponse.data.result?.tools || []; logger.log(`Found ${tools.length} tools`, { toolNames: tools.map(t => t.name).slice(0, 5) }); // Step 6: Test actual tool call - Get AWS costs logger.log('\n📋 Step 6: Testing actual tool call - AWS monthly costs'); // Get current month dates const now = new Date(); const year = now.getFullYear(); const month = String(now.getMonth() + 1).padStart(2, '0'); const startDate = `${year}-${month}-01`; const endDate = now.toISOString().split('T')[0]; logger.log(`Querying AWS costs for period: ${startDate} to ${endDate}`); const costResponse = await axios.post( `${TEST_CONFIG.tunnelUrl}/mcp`, { jsonrpc: '2.0', method: 'tools/call', params: { name: 'api___invoices_caui', arguments: { accountId: '932213950603', startDate: startDate, endDate: endDate, periodGranLevel: 'month', groupBy: 'none', cloud_context: 'aws' } }, id: 3 }, { headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' } } ); // Parse the response const costResult = costResponse.data.result; if (costResult && costResult.content && costResult.content[0]) { const costData = JSON.parse(costResult.content[0].text); logger.log('✅ AWS Cost Query SUCCESSFUL!', { period: `${startDate} to ${endDate}`, totalCost: costData.data?.[0]?.cost || costData.totalCost || 'N/A', currency: costData.data?.[0]?.currency || costData.currency || 'USD', dataPoints: costData.data?.length || 0, responseStructure: { hasData: !!costData.data, hasError: !!costData.error, hasTotalCost: !!costData.totalCost } }); // Show detailed cost breakdown if available if (costData.data && Array.isArray(costData.data)) { logger.log('Cost breakdown:', costData.data.map(item => ({ date: item.date || item.period, cost: item.cost, currency: item.currency }))); } } else { logger.log('⚠️ Unexpected response format', costResponse.data); } // Step 7: Test another query - costs by service logger.log('\n📋 Step 7: Testing AWS costs by service'); const serviceResponse = await axios.post( `${TEST_CONFIG.tunnelUrl}/mcp`, { jsonrpc: '2.0', method: 'tools/call', params: { name: 'api___invoices_caui', arguments: { accountId: '932213950603', startDate: startDate, endDate: endDate, periodGranLevel: 'month', groupBy: 'service', cloud_context: 'aws' } }, id: 4 }, { headers: { 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json' } } ); const serviceResult = serviceResponse.data.result; if (serviceResult && serviceResult.content && serviceResult.content[0]) { const serviceData = JSON.parse(serviceResult.content[0].text); logger.log('✅ AWS Service Breakdown Query SUCCESSFUL!', { period: `${startDate} to ${endDate}`, totalServices: serviceData.data?.length || 0, topServices: serviceData.data?.slice(0, 5).map(s => ({ service: s.service || s.groupBy, cost: s.cost, currency: s.currency })) }); } // Final success message logger.log('\n' + '='.repeat(80)); logger.log('🎉 ALL TESTS PASSED! Bearer authentication and MCP tools working correctly!'); logger.log('='.repeat(80)); } catch (error) { logger.error('Test failed', error); if (error.response) { logger.error('Response details', { status: error.response.status, statusText: error.response.statusText, data: error.response.data }); } } // Print summary logger.summary(); } // Run the test console.log('🚀 Starting Comprehensive OAuth + Bearer + MCP Test Suite'); console.log('='.repeat(80)); console.log(`Target: ${TEST_CONFIG.tunnelUrl}`); console.log(`User: ${TEST_CONFIG.credentials.username}`); console.log('='.repeat(80) + '\n'); runComprehensiveTest().catch(error => { console.error('Fatal error:', error); process.exit(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/daviddraiumbrella/invoice-monitoring'

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