Skip to main content
Glama
test-bank-leumi-oauth.cjs13 kB
const { createHash, randomBytes } = require('crypto'); const express = require('express'); const { spawn } = require('child_process'); const https = require('https'); const fetch = require('node-fetch'); // Configure to ignore self-signed certificates for localhost process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0; const LOCAL_MCP_URL = 'https://localhost:3003'; function base64URLEncode(str) { return str.toString('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, ''); } function generateCodeChallenge() { const codeVerifier = base64URLEncode(randomBytes(32)); const codeChallenge = base64URLEncode(createHash('sha256').update(codeVerifier).digest()); return { codeVerifier, codeChallenge }; } class BankLeumiOAuthTest { constructor() { this.username = 'david+allcloud@umbrellacost.com'; this.password = 'Dsamsung1!'; this.accessToken = null; this.server = null; this.port = 3004; // Port that's allowed in TestUmbrellaMCP config this.codeVerifier = null; } async startCallbackServer() { return new Promise((resolve) => { const app = express(); app.get('/', (req, res) => { res.send(` <html> <head><title>Bank Leumi OAuth Test</title></head> <body style="font-family: Arial; text-align: center; padding: 50px;"> <h1>🏦 Bank Leumi OAuth Test</h1> <p><strong>Testing with userQuery parameter</strong></p> </body> </html> `); }); app.get('/callback', (req, res) => { const { code, error, state } = req.query; if (error) { res.send(`<html><body>Error: ${error}</body></html>`); return; } if (code) { res.send(` <html> <body style="font-family: Arial; text-align: center; padding: 50px;"> <h1 style="color: green;">✅ OAuth Success!</h1> <p>Testing Bank Leumi recommendations...</p> <script>setTimeout(() => window.close(), 2000);</script> </body> </html> `); resolve(code); } }); this.server = app.listen(this.port, () => { console.log(`📡 Callback server listening on port ${this.port}`); }); }); } async registerOAuthClient() { // Use localhost on port 3004 as allowed in config const redirectUri = `http://localhost:${this.port}/callback`; const clientData = { client_name: 'Bank Leumi OAuth Test Client', redirect_uris: [redirectUri], grant_types: ['authorization_code'], response_types: ['code'], scope: 'openid profile email' }; const response = await fetch(`${LOCAL_MCP_URL}/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(clientData), agent: new https.Agent({ rejectUnauthorized: false }) }); if (!response.ok) { throw new Error(`Client registration failed: ${response.status}`); } const client = await response.json(); console.log(`✅ Client registered: ${client.client_id}`); return { clientId: client.client_id, redirectUri }; } async performOAuthFlow(clientId, redirectUri) { const { codeVerifier, codeChallenge } = generateCodeChallenge(); this.codeVerifier = codeVerifier; const authUrl = `${LOCAL_MCP_URL}/authorize?` + new URLSearchParams({ response_type: 'code', client_id: clientId, redirect_uri: redirectUri, state: 'bank_leumi_oauth_test', code_challenge: codeChallenge, code_challenge_method: 'S256', scope: 'openid profile email' }); console.log('🌐 Opening browser for authentication...'); this.openBrowser(authUrl); return true; } openBrowser(url) { const platform = process.platform; let command; if (platform === 'darwin') { command = 'open'; } else if (platform === 'win32') { command = 'start'; } else { command = 'xdg-open'; } spawn(command, [url], { detached: true, stdio: 'ignore' }); } async exchangeCodeForToken(authorizationCode, clientId, redirectUri) { const response = await fetch(`${LOCAL_MCP_URL}/oauth/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'authorization_code', code: authorizationCode, redirect_uri: redirectUri, client_id: clientId, code_verifier: this.codeVerifier }), agent: new https.Agent({ rejectUnauthorized: false }) }); if (response.ok) { const tokenData = await response.json(); this.accessToken = tokenData.access_token; console.log(`✅ Token obtained successfully`); return tokenData; } else { const error = await response.text(); throw new Error(`Token exchange failed: ${error}`); } } async testBankLeumiRecommendations() { if (!this.accessToken) { console.log('❌ No bearer token available'); return false; } console.log(`\n🚀 Testing Bank Leumi recommendations with userQuery`); console.log(`🔑 Bearer Token: ${this.accessToken.substring(0, 50)}...`); try { // Test 1: Bank Leumi BL Test Env with userQuery console.log('\n📊 Test 1: Bank Leumi BL Test Env - with userQuery'); console.log('Expected: Should use account key 24223'); console.log('Expected API key format: 57ade50e-c9a8-49f3-8ce7-28d44536a669:24223:1'); const request1 = { jsonrpc: '2.0', method: 'tools/call', params: { name: 'get_all_recommendations', arguments: { userQuery: 'show me recommendations for Bank Leumi BL Test Env', daysBack: 30, pageSize: 100, includeClosedAndDone: false } }, id: 1 }; console.log('📤 Request:', JSON.stringify(request1.params.arguments, null, 2)); const startTime1 = Date.now(); const response1 = await fetch(`${LOCAL_MCP_URL}/mcp`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.accessToken}`, 'Content-Type': 'application/json', 'Accept': 'application/json, text/event-stream' }, body: JSON.stringify(request1), agent: new https.Agent({ rejectUnauthorized: false }) }); const time1 = Date.now() - startTime1; if (response1.ok) { let data1; const contentType = response1.headers.get('content-type'); if (contentType && contentType.includes('text/event-stream')) { const responseText = await response1.text(); const lines = responseText.split('\n'); for (const line of lines) { if (line.startsWith('data: ')) { const jsonStr = line.substring(6).trim(); if (jsonStr && jsonStr !== '[DONE]') { try { data1 = JSON.parse(jsonStr); break; } catch (parseError) {} } } } } else { data1 = await response1.json(); } console.log(`✅ Test 1 SUCCESS (${time1}ms)`); if (data1 && data1.result && data1.result.content && data1.result.content[0]) { const text = data1.result.content[0].text; const customerMatch = text.match(/\*\*Customer:\*\* ([^\n]+)/); const totalMatch = text.match(/\*\*Total Recommendations:\*\* (\d+)/); const savingsMatch = text.match(/\*\*Total Potential Savings:\*\* \$[\d,]+\.\d{2}/); if (customerMatch) { console.log(` Detected Customer: ${customerMatch[1]}`); } if (totalMatch) { console.log(` Total Recommendations: ${totalMatch[1]}`); } if (savingsMatch) { console.log(` ${savingsMatch[0].replace(/\*\*/g, '')}`); } // Show first few recommendations const recsMatch = text.match(/## Top \d+ Recommendations by Savings:[\s\S]*?(?=##|$)/); if (recsMatch) { console.log('\n First few recommendations:'); const recs = recsMatch[0].split(/\d+\.\s+\*\*/).slice(1, 4); recs.forEach((rec, i) => { const lines = rec.split('\n').filter(l => l.trim()); if (lines[0]) { console.log(` ${i + 1}. ${lines[0].replace(/\*\*/g, '').trim()}`); } }); } } } else { console.log(`❌ Test 1 FAILED: ${response1.status}`); const error = await response1.text(); console.log('Error:', error); } // Test 2: Without userQuery (should default to first account) console.log('\n📊 Test 2: Without userQuery - should use default account'); const request2 = { jsonrpc: '2.0', method: 'tools/call', params: { name: 'get_all_recommendations', arguments: { daysBack: 7, pageSize: 50 } }, id: 2 }; console.log('📤 Request:', JSON.stringify(request2.params.arguments, null, 2)); const startTime2 = Date.now(); const response2 = await fetch(`${LOCAL_MCP_URL}/mcp`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.accessToken}`, 'Content-Type': 'application/json', 'Accept': 'application/json, text/event-stream' }, body: JSON.stringify(request2), agent: new https.Agent({ rejectUnauthorized: false }) }); const time2 = Date.now() - startTime2; if (response2.ok) { let data2; const contentType = response2.headers.get('content-type'); if (contentType && contentType.includes('text/event-stream')) { const responseText = await response2.text(); const lines = responseText.split('\n'); for (const line of lines) { if (line.startsWith('data: ')) { const jsonStr = line.substring(6).trim(); if (jsonStr && jsonStr !== '[DONE]') { try { data2 = JSON.parse(jsonStr); break; } catch (parseError) {} } } } } else { data2 = await response2.json(); } console.log(`✅ Test 2 SUCCESS (${time2}ms)`); if (data2 && data2.result && data2.result.content && data2.result.content[0]) { const text = data2.result.content[0].text; const customerMatch = text.match(/\*\*Customer:\*\* ([^\n]+)/); if (customerMatch) { console.log(` Default Customer: ${customerMatch[1]}`); } } } else { console.log(`❌ Test 2 FAILED: ${response2.status}`); } console.log('\n✅ Bank Leumi OAuth Tests Complete!'); console.log('\n📝 Check server logs for:'); console.log(' - Customer detection from userQuery'); console.log(' - API key generation with correct format'); return true; } catch (error) { console.log('❌ Test Error:', error.message); return false; } } cleanup() { if (this.server) { this.server.close(); } } async run() { try { console.log('🏦 Bank Leumi OAuth Test Starting...\n'); const serverPromise = this.startCallbackServer(); const { clientId, redirectUri } = await this.registerOAuthClient(); const flowStarted = await this.performOAuthFlow(clientId, redirectUri); if (!flowStarted) { throw new Error('Failed to start OAuth flow'); } console.log('⏳ Waiting for authentication...'); console.log(' Please login with: david+allcloud@umbrellacost.com'); const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('OAuth timeout after 5 minutes')), 300000) ); const authorizationCode = await Promise.race([serverPromise, timeoutPromise]); if (authorizationCode) { const tokenData = await this.exchangeCodeForToken(authorizationCode, clientId, redirectUri); if (tokenData) { await this.testBankLeumiRecommendations(); } } } catch (error) { console.error('❌ Error:', error.message); } finally { this.cleanup(); } } } // Handle graceful shutdown process.on('SIGINT', () => { console.log('\n🛑 Interrupted - cleaning up...'); process.exit(0); }); const test = new BankLeumiOAuthTest(); test.run();

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