Files
gds-mock-mcp/tests/fixtures/remote-client.js

156 lines
4.4 KiB
JavaScript

/**
* Example MCP Remote Client
* Demonstrates SSE connection, Last-Event-ID resumption, and tool invocation
*
* Usage: node tests/fixtures/remote-client.js
*/
import { EventSource } from 'eventsource';
const MCP_SERVER_URL = process.env.MCP_SERVER_URL || 'http://127.0.0.1:3000';
const MCP_PROTOCOL_VERSION = '2025-06-18';
/**
* Example: Connect to MCP server and invoke searchFlights tool
*/
async function exampleClient() {
console.log(`Connecting to MCP server at ${MCP_SERVER_URL}...`);
// 1. Initialize connection - send initialize request
// Per MCP spec: MCP-Protocol-Version header is NOT required during initialization
const initRequest = {
jsonrpc: '2.0',
id: 0,
method: 'initialize',
params: {
protocolVersion: MCP_PROTOCOL_VERSION,
capabilities: {},
clientInfo: {
name: 'gds-mock-test-client',
version: '1.0.0'
}
}
};
console.log('Sending initialize request (without MCP-Protocol-Version header)...');
const initResponse = await fetch(`${MCP_SERVER_URL}/mcp`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json, text/event-stream'
// Note: MCP-Protocol-Version header NOT included during initialization
},
body: JSON.stringify(initRequest)
});
const initResult = await initResponse.json();
console.log('Server initialized:', JSON.stringify(initResult, null, 2));
// Note: Server runs in stateless mode (no MCP-Session-Id header will be present)
// Each client can initialize independently without server-side session management
const sessionId = initResponse.headers.get('MCP-Session-Id');
if (sessionId) {
console.log('Session ID:', sessionId);
} else {
console.log('Server running in stateless mode (no session ID)');
}
// 2. Open SSE stream for server messages (now with MCP-Protocol-Version header)
const eventSource = new EventSource(`${MCP_SERVER_URL}/mcp`, {
headers: {
'MCP-Protocol-Version': MCP_PROTOCOL_VERSION,
...(sessionId && { 'MCP-Session-Id': sessionId })
}
});
let lastEventId = null;
// Handle SSE events
eventSource.addEventListener('message', (event) => {
console.log('Received SSE event:', event.data);
lastEventId = event.lastEventId;
try {
const data = JSON.parse(event.data);
// Extract session ID from InitializeResult
if (data.result && data.result.protocolVersion) {
console.log('Server initialized:', data.result);
}
} catch {
// Empty data or non-JSON - expected per SSE polling pattern
}
});
eventSource.addEventListener('error', (error) => {
console.error('SSE error:', error);
});
// Wait for connection
await new Promise(resolve => setTimeout(resolve, 1000));
// 2. Send tool invocation via POST
const searchRequest = {
jsonrpc: '2.0',
id: 1,
method: 'tools/call',
params: {
name: 'searchFlights',
arguments: {
origin: 'JFK',
destination: 'LAX',
departureDate: '2026-05-01',
passengers: 1,
cabin: 'economy'
}
}
};
console.log('Sending searchFlights request...');
const response = await fetch(`${MCP_SERVER_URL}/mcp`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'MCP-Protocol-Version': MCP_PROTOCOL_VERSION,
...(sessionId && { 'MCP-Session-Id': sessionId }),
...(lastEventId && { 'Last-Event-ID': lastEventId })
},
body: JSON.stringify(searchRequest)
});
const result = await response.json();
console.log('Search results:', JSON.stringify(result, null, 2));
// 3. Demonstrate Last-Event-ID resumption
if (lastEventId) {
console.log(`\nReconnecting with Last-Event-ID: ${lastEventId}...`);
const resumedEventSource = new EventSource(`${MCP_SERVER_URL}/mcp`, {
headers: {
'MCP-Protocol-Version': MCP_PROTOCOL_VERSION,
'Last-Event-ID': lastEventId
}
});
resumedEventSource.addEventListener('message', (event) => {
console.log('Resumed stream event:', event.data);
});
// Close after demonstration
setTimeout(() => {
resumedEventSource.close();
eventSource.close();
console.log('\nClient demonstration complete');
process.exit(0);
}, 2000);
}
}
// Run example
exampleClient().catch(err => {
console.error('Client error:', err);
process.exit(1);
});