156 lines
4.4 KiB
JavaScript
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);
|
|
});
|