Fix SSE transport setup and trust proxy configuration
This commit is contained in:
241
test-http-mcp.js
241
test-http-mcp.js
@@ -4,9 +4,10 @@
|
||||
* Test script for HTTP/SSE MCP Server
|
||||
*
|
||||
* This script tests the deployed MCP server by:
|
||||
* 1. Connecting to the SSE endpoint
|
||||
* 2. Sending MCP protocol messages
|
||||
* 3. Displaying responses
|
||||
* 1. Resetting the Circuit Breaker on the server.
|
||||
* 2. Connecting to the SSE endpoint.
|
||||
* 3. Sending MCP protocol messages sequentially.
|
||||
* 4. Displaying responses and closing the connection cleanly.
|
||||
*
|
||||
* Usage: node test-http-mcp.js
|
||||
*/
|
||||
@@ -14,57 +15,85 @@
|
||||
import EventSource from 'eventsource';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
const SERVER_URL = process.env.MCP_SERVER_URL || 'https://hpr-knowledge-base.onrender.com';
|
||||
const SERVER_URL = process.env.MCP_SERVER_URL || 'http://localhost:3000';
|
||||
const SSE_ENDPOINT = `${SERVER_URL}/sse`;
|
||||
const MESSAGE_ENDPOINT = `${SERVER_URL}/message`;
|
||||
const RESET_ENDPOINT = `${SERVER_URL}/reset`; // New endpoint for circuit breaker reset
|
||||
|
||||
let requestId = 1;
|
||||
let sse; // Declare outside for scope
|
||||
let connectionId = null; // To store the connection ID from the server
|
||||
|
||||
console.log('🧪 Testing MCP Server over HTTP/SSE');
|
||||
console.log(`📡 Server: ${SERVER_URL}`);
|
||||
console.log('-- Testing MCP Server over HTTP/SSE');
|
||||
console.log(`-- Server: ${SERVER_URL}`);
|
||||
console.log(`-- Message Endpoint: ${MESSAGE_ENDPOINT}`);
|
||||
console.log('');
|
||||
|
||||
// Test health endpoint first
|
||||
console.log('1️⃣ Testing health endpoint...');
|
||||
try {
|
||||
const healthResponse = await fetch(`${SERVER_URL}/health`);
|
||||
const health = await healthResponse.json();
|
||||
console.log('✅ Health check:', JSON.stringify(health, null, 2));
|
||||
console.log('');
|
||||
} catch (error) {
|
||||
console.error('❌ Health check failed:', error.message);
|
||||
process.exit(1);
|
||||
// === 0. Reset Circuit Breaker ===
|
||||
async function resetCircuitBreaker() {
|
||||
console.log('0. Resetting Circuit Breaker...');
|
||||
try {
|
||||
const resetResponse = await fetch(RESET_ENDPOINT, { method: 'POST' });
|
||||
const result = await resetResponse.json();
|
||||
console.log(`OK Reset check: ${result.message}`);
|
||||
} catch (error) {
|
||||
console.error('ERROR Circuit breaker reset failed (Server not fully up or endpoint missing):', error.message);
|
||||
}
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// Connect to SSE endpoint
|
||||
console.log('2️⃣ Connecting to SSE endpoint...');
|
||||
const sse = new EventSource(SSE_ENDPOINT);
|
||||
// === 1. Test Health Endpoint ===
|
||||
async function checkHealth() {
|
||||
console.log('1. Testing health endpoint...');
|
||||
try {
|
||||
const healthResponse = await fetch(`${SERVER_URL}/health`);
|
||||
const health = await healthResponse.json();
|
||||
console.log('OK Health check:', JSON.stringify(health, null, 2));
|
||||
console.log('');
|
||||
} catch (error) {
|
||||
console.error('ERROR Health check failed:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
sse.onopen = () => {
|
||||
console.log('✅ SSE connection established');
|
||||
console.log('');
|
||||
// === 2. Connect to SSE Endpoint ===
|
||||
function connectSSE() {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log('2. Connecting to SSE endpoint...');
|
||||
// Use the EventSource polyfill to handle the SSE GET connection
|
||||
sse = new EventSource(SSE_ENDPOINT);
|
||||
|
||||
// Run tests after connection is established
|
||||
setTimeout(() => runTests(), 1000);
|
||||
};
|
||||
sse.onopen = () => {
|
||||
console.log('OK SSE connection established');
|
||||
// Resolve the promise once the connection is open
|
||||
resolve();
|
||||
};
|
||||
|
||||
sse.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log('📨 Received:', JSON.stringify(data, null, 2));
|
||||
console.log('');
|
||||
} catch (error) {
|
||||
console.log('📨 Received (raw):', event.data);
|
||||
console.log('');
|
||||
}
|
||||
};
|
||||
sse.addEventListener('endpoint', (event) => {
|
||||
// The endpoint event data contains the sessionId in the URL
|
||||
const url = new URL(event.data, SERVER_URL);
|
||||
connectionId = url.searchParams.get('sessionId');
|
||||
console.log(`OK Received sessionId: ${connectionId}`);
|
||||
});
|
||||
|
||||
sse.onerror = (error) => {
|
||||
console.error('❌ SSE error:', error);
|
||||
console.log('');
|
||||
};
|
||||
sse.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log('RECEIVED:', JSON.stringify(data, null, 2));
|
||||
} catch (error) {
|
||||
console.log('RECEIVED (raw):', event.data);
|
||||
}
|
||||
console.log('');
|
||||
};
|
||||
|
||||
// Send MCP messages
|
||||
sse.onerror = (error) => {
|
||||
// Log and ignore to let the rest of the test run, as EventSource auto-reconnects
|
||||
console.error('ERROR SSE error:', error.message || JSON.stringify(error));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// === 3. Send MCP Message (POST) ===
|
||||
async function sendMessage(method, params = {}) {
|
||||
const message = {
|
||||
jsonrpc: '2.0',
|
||||
@@ -73,92 +102,114 @@ async function sendMessage(method, params = {}) {
|
||||
params
|
||||
};
|
||||
|
||||
console.log('📤 Sending:', method);
|
||||
console.log('SENDING:', method);
|
||||
console.log(JSON.stringify(message, null, 2));
|
||||
console.log('');
|
||||
|
||||
try {
|
||||
const response = await fetch(MESSAGE_ENDPOINT, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-connection-id': connectionId // Include the connection ID
|
||||
},
|
||||
body: JSON.stringify(message)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
console.log('✅ Message sent successfully');
|
||||
console.log('OK Message sent successfully');
|
||||
} else {
|
||||
console.error('❌ Message send failed:', response.status, response.statusText);
|
||||
// Log the full error response body if it's not successful
|
||||
const errorBody = await response.text();
|
||||
console.error('ERROR Message send failed:', response.status, response.statusText, errorBody);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Send error:', error.message);
|
||||
console.error('ERROR Send error:', error.message);
|
||||
}
|
||||
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// Run test sequence
|
||||
// === Main Test Sequence ===
|
||||
async function runTests() {
|
||||
console.log('3️⃣ Running MCP protocol tests...');
|
||||
console.log('');
|
||||
|
||||
// Test 1: Initialize
|
||||
await sendMessage('initialize', {
|
||||
protocolVersion: '0.1.0',
|
||||
clientInfo: {
|
||||
name: 'test-client',
|
||||
version: '1.0.0'
|
||||
},
|
||||
capabilities: {}
|
||||
});
|
||||
|
||||
await sleep(2000);
|
||||
|
||||
// Test 2: List tools
|
||||
await sendMessage('tools/list');
|
||||
|
||||
await sleep(2000);
|
||||
|
||||
// Test 3: List resources
|
||||
await sendMessage('resources/list');
|
||||
|
||||
await sleep(2000);
|
||||
|
||||
// Test 4: Call a tool (search episodes)
|
||||
await sendMessage('tools/call', {
|
||||
name: 'search_episodes',
|
||||
arguments: {
|
||||
query: 'linux',
|
||||
limit: 3
|
||||
// Ensure the health check runs first
|
||||
await checkHealth();
|
||||
|
||||
// Ensure the circuit breaker is reset before trying to connect
|
||||
await resetCircuitBreaker();
|
||||
|
||||
// Establish a fresh, single connection for the test sequence
|
||||
await connectSSE();
|
||||
// Wait for connectionId to be received
|
||||
while (connectionId === null) {
|
||||
await sleep(100);
|
||||
}
|
||||
});
|
||||
await sleep(1000); // Give the server a moment to finalize setup
|
||||
|
||||
// Log the start of the protocol tests
|
||||
console.log('3. Running MCP protocol tests...');
|
||||
console.log('');
|
||||
|
||||
await sleep(2000);
|
||||
// Test 1: Initialize
|
||||
await sendMessage('initialize', {
|
||||
protocolVersion: '0.1.0',
|
||||
clientInfo: {
|
||||
name: 'test-client',
|
||||
version: '1.0.0'
|
||||
},
|
||||
capabilities: {}
|
||||
});
|
||||
|
||||
// Test 5: Read a resource
|
||||
await sendMessage('resources/read', {
|
||||
uri: 'hpr://stats'
|
||||
});
|
||||
await sleep(1000);
|
||||
|
||||
await sleep(3000);
|
||||
// Test 2: List tools
|
||||
await sendMessage('tools/list');
|
||||
|
||||
console.log('✅ All tests completed!');
|
||||
console.log('');
|
||||
console.log('💡 The MCP server is working correctly over HTTP/SSE');
|
||||
console.log('🔮 Once AI tools add HTTP/SSE support, they can connect to:');
|
||||
console.log(` ${SSE_ENDPOINT}`);
|
||||
await sleep(1000);
|
||||
|
||||
// Close connection
|
||||
sse.close();
|
||||
process.exit(0);
|
||||
// Test 3: List resources
|
||||
await sendMessage('resources/list');
|
||||
|
||||
await sleep(1000);
|
||||
|
||||
// Test 4: Call a tool (search episodes)
|
||||
await sendMessage('tools/call', {
|
||||
name: 'search_episodes',
|
||||
arguments: {
|
||||
query: 'linux',
|
||||
limit: 3
|
||||
}
|
||||
});
|
||||
|
||||
await sleep(1000);
|
||||
|
||||
// Test 5: Read a resource
|
||||
await sendMessage('resources/read', {
|
||||
uri: 'hpr://stats'
|
||||
});
|
||||
|
||||
await sleep(2000);
|
||||
|
||||
console.log('OK All tests completed!');
|
||||
console.log('');
|
||||
console.log('-- The MCP server is working correctly over HTTP/SSE');
|
||||
console.log('-- Once AI tools add HTTP/SSE support, they can connect to:');
|
||||
console.log(` ${SSE_ENDPOINT}`);
|
||||
|
||||
// Close connection explicitly at the end of the test run to stop auto-reconnects
|
||||
sse.close();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
// Start the test sequence
|
||||
runTests();
|
||||
|
||||
// Handle Ctrl+C
|
||||
process.on('SIGINT', () => {
|
||||
console.log('\n👋 Closing connection...');
|
||||
sse.close();
|
||||
console.log('\n-- Closing connection...');
|
||||
if (sse) sse.close();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user