import { serve } from '@hono/node-server';
import { Hono } from 'hono';
import sodium from 'libsodium-wrappers';
import axios from 'axios';
import { EventSource } from 'eventsource';
// ---------------------------------------------------------
// 1. Mock BPP (The "Uber/Namma Yatri" Server)
// ---------------------------------------------------------
const bppApp = new Hono();
bppApp.post('/search', async (c) => {
console.log('🚖 [BPP] Received Search Request from MCP Server');
const body = await c.req.json();
// Verify Signature Header Presence
if (!c.req.header('Authorization')) {
console.error('❌ [BPP] Missing Authorization Header');
return c.json({ error: 'Unauthorized' }, 401);
}
// Simulate Async Processing & Webhook Callback
setTimeout(async () => {
console.log('📞 [BPP] Found a driver! Sending webhook to MCP Server...');
try {
await axios.post('http://localhost:3000/on_search', {
context: { ...body.context, action: 'on_search' },
message: {
catalog: {
providers: [{
id: 'driver-1',
descriptor: { name: 'Real Test Driver' },
items: [{ id: 'ride-1', price: { value: '150', currency: 'INR' } }]
}]
}
}
});
} catch (e) {
console.error('❌ [BPP] Webhook Failed:', e.message);
}
}, 500);
return c.json({ message: { ack: { status: 'ACK' } } });
});
// ---------------------------------------------------------
// 2. The Test Runner (Simulating "Claude Desktop")
// ---------------------------------------------------------
async function runBlackBoxTest() {
await sodium.ready;
console.log('📦 Starting Black Box End-to-End Test...');
// Start BPP
const bppServer = serve({ fetch: bppApp.fetch, port: 3001 });
console.log('✅ [BPP] Running on :3001');
// Start MCP Server (The System Under Test)
// We set NODE_ENV=test to prevent auto-start in index.ts, but we start it manually here
process.env.NODE_ENV = 'test'; // Prevent auto-start
process.env.BPP_URI = 'http://localhost:3001'; // Point to our mock BPP
process.env.BAP_URI = 'http://localhost:3000';
process.env.BAP_ID = 'test-bap';
// Generate valid keys
const keyPair = sodium.crypto_sign_keypair();
process.env.SIGNING_PRIVATE_KEY = sodium.to_base64(keyPair.privateKey, sodium.base64_variants.ORIGINAL);
process.env.SIGNING_PUBLIC_KEY = sodium.to_base64(keyPair.publicKey, sodium.base64_variants.ORIGINAL);
process.env.UNIQUE_KEY_ID = 'key1';
const { app: mcpApp } = await import('./index.js');
const mcpServer = serve({ fetch: mcpApp.fetch, port: 3000 });
console.log('✅ [MCP] Running on :3000');
// --- THE CLIENT LOGIC ---
console.log('🤖 [Client] Connecting to SSE stream...');
const eventSource = new EventSource('http://localhost:3000/sse');
let sessionId = '';
let messageEndpoint = '';
eventSource.addEventListener('endpoint', (event: any) => {
const url = new URL(event.data, 'http://localhost:3000');
sessionId = url.searchParams.get('sessionId') || '';
messageEndpoint = `http://localhost:3000${event.data}`;
console.log(`🔗 [Client] Received Endpoint: ${messageEndpoint} (Session: ${sessionId})`);
// Once connected, send the Tool Call
performToolCall(messageEndpoint);
});
eventSource.addEventListener('message', (event: any) => {
const data = JSON.parse(event.data);
console.log('📩 [Client] Received SSE Message:', JSON.stringify(data));
if (data.result && data.result.content) {
const text = data.result.content[0].text;
if (text.includes('Real Test Driver')) {
console.log('✅✅✅ SUCCESS: Full End-to-End Flow Verified!');
console.log(' 1. Client connected via SSE');
console.log(' 2. Client called tool via HTTP POST');
console.log(' 3. Server signed request -> BPP');
console.log(' 4. BPP sent webhook -> Server');
console.log(' 5. Server correlated response -> Client via SSE');
cleanup();
process.exit(0);
}
}
});
eventSource.onerror = (err) => {
console.error('❌ [Client] SSE Error:', err);
};
async function performToolCall(endpoint: string) {
console.log('🛠️ [Client] Calling Tool: search_cabs...');
const request = {
jsonrpc: '2.0',
id: 1,
method: 'tools/call',
params: {
name: 'search_cabs',
arguments: {
start_gps: '12.9716,77.5946',
end_gps: '12.9352,77.6245'
}
}
};
try {
await axios.post(endpoint, request);
console.log('Dg [Client] Tool Call Sent. Waiting for result...');
} catch (e) {
console.error('❌ [Client] Tool Call Failed:', e.message);
cleanup();
process.exit(1);
}
}
function cleanup() {
eventSource.close();
bppServer.close();
mcpServer.close();
}
// Timeout Safety
setTimeout(() => {
console.error('❌ [Timeout] Test took too long!');
cleanup();
process.exit(1);
}, 10000);
}
runBlackBoxTest().catch(console.error);