test-protocol-negotiation.tsβ’6.09 kB
#!/usr/bin/env node
/**
* Test Protocol Version Negotiation
*
* This script tests the protocol version negotiation logic with different client scenarios.
*/
import {
negotiateProtocolVersion,
isN8nClient,
STANDARD_PROTOCOL_VERSION,
N8N_PROTOCOL_VERSION
} from '../utils/protocol-version';
interface TestCase {
name: string;
clientVersion?: string;
clientInfo?: any;
userAgent?: string;
headers?: Record<string, string>;
expectedVersion: string;
expectedIsN8nClient: boolean;
}
const testCases: TestCase[] = [
{
name: 'Standard MCP client (Claude Desktop)',
clientVersion: '2025-03-26',
clientInfo: { name: 'Claude Desktop', version: '1.0.0' },
expectedVersion: '2025-03-26',
expectedIsN8nClient: false
},
{
name: 'n8n client with specific client info',
clientVersion: '2025-03-26',
clientInfo: { name: 'n8n', version: '1.0.0' },
expectedVersion: N8N_PROTOCOL_VERSION,
expectedIsN8nClient: true
},
{
name: 'LangChain client',
clientVersion: '2025-03-26',
clientInfo: { name: 'langchain-js', version: '0.1.0' },
expectedVersion: N8N_PROTOCOL_VERSION,
expectedIsN8nClient: true
},
{
name: 'n8n client via user agent',
clientVersion: '2025-03-26',
userAgent: 'n8n/1.0.0',
expectedVersion: N8N_PROTOCOL_VERSION,
expectedIsN8nClient: true
},
{
name: 'n8n mode environment variable',
clientVersion: '2025-03-26',
expectedVersion: N8N_PROTOCOL_VERSION,
expectedIsN8nClient: true
},
{
name: 'Client requesting older version',
clientVersion: '2024-06-25',
clientInfo: { name: 'Some Client', version: '1.0.0' },
expectedVersion: '2024-06-25',
expectedIsN8nClient: false
},
{
name: 'Client requesting unsupported version',
clientVersion: '2020-01-01',
clientInfo: { name: 'Old Client', version: '1.0.0' },
expectedVersion: STANDARD_PROTOCOL_VERSION,
expectedIsN8nClient: false
},
{
name: 'No client info provided',
expectedVersion: STANDARD_PROTOCOL_VERSION,
expectedIsN8nClient: false
},
{
name: 'n8n headers detection',
clientVersion: '2025-03-26',
headers: { 'x-n8n-version': '1.0.0' },
expectedVersion: N8N_PROTOCOL_VERSION,
expectedIsN8nClient: true
}
];
async function runTests(): Promise<void> {
console.log('π§ͺ Testing Protocol Version Negotiation\n');
let passed = 0;
let failed = 0;
// Set N8N_MODE for the environment variable test
const originalN8nMode = process.env.N8N_MODE;
for (const testCase of testCases) {
try {
// Set N8N_MODE for specific test
if (testCase.name.includes('environment variable')) {
process.env.N8N_MODE = 'true';
} else {
delete process.env.N8N_MODE;
}
// Test isN8nClient function
const detectedAsN8n = isN8nClient(testCase.clientInfo, testCase.userAgent, testCase.headers);
// Test negotiateProtocolVersion function
const result = negotiateProtocolVersion(
testCase.clientVersion,
testCase.clientInfo,
testCase.userAgent,
testCase.headers
);
// Check results
const versionCorrect = result.version === testCase.expectedVersion;
const n8nDetectionCorrect = result.isN8nClient === testCase.expectedIsN8nClient;
const isN8nFunctionCorrect = detectedAsN8n === testCase.expectedIsN8nClient;
if (versionCorrect && n8nDetectionCorrect && isN8nFunctionCorrect) {
console.log(`β
${testCase.name}`);
console.log(` Version: ${result.version}, n8n client: ${result.isN8nClient}`);
console.log(` Reasoning: ${result.reasoning}\n`);
passed++;
} else {
console.log(`β ${testCase.name}`);
console.log(` Expected: version=${testCase.expectedVersion}, isN8n=${testCase.expectedIsN8nClient}`);
console.log(` Got: version=${result.version}, isN8n=${result.isN8nClient}`);
console.log(` isN8nClient function: ${detectedAsN8n} (expected: ${testCase.expectedIsN8nClient})`);
console.log(` Reasoning: ${result.reasoning}\n`);
failed++;
}
} catch (error) {
console.log(`π₯ ${testCase.name} - ERROR`);
console.log(` ${error instanceof Error ? error.message : String(error)}\n`);
failed++;
}
}
// Restore original N8N_MODE
if (originalN8nMode) {
process.env.N8N_MODE = originalN8nMode;
} else {
delete process.env.N8N_MODE;
}
// Summary
console.log(`\nπ Test Results:`);
console.log(` β
Passed: ${passed}`);
console.log(` β Failed: ${failed}`);
console.log(` Total: ${passed + failed}`);
if (failed > 0) {
console.log(`\nβ Some tests failed!`);
process.exit(1);
} else {
console.log(`\nπ All tests passed!`);
}
}
// Additional integration test
async function testIntegration(): Promise<void> {
console.log('\nπ§ Integration Test - MCP Server Protocol Negotiation\n');
// This would normally test the actual MCP server, but we'll just verify
// the negotiation logic works in typical scenarios
const scenarios = [
{
name: 'Claude Desktop connecting',
clientInfo: { name: 'Claude Desktop', version: '1.0.0' },
clientVersion: '2025-03-26'
},
{
name: 'n8n connecting via HTTP',
headers: { 'user-agent': 'n8n/1.52.0' },
clientVersion: '2025-03-26'
}
];
for (const scenario of scenarios) {
const result = negotiateProtocolVersion(
scenario.clientVersion,
scenario.clientInfo,
scenario.headers?.['user-agent'],
scenario.headers
);
console.log(`π ${scenario.name}:`);
console.log(` Negotiated version: ${result.version}`);
console.log(` Is n8n client: ${result.isN8nClient}`);
console.log(` Reasoning: ${result.reasoning}\n`);
}
}
if (require.main === module) {
runTests()
.then(() => testIntegration())
.catch(error => {
console.error('Test execution failed:', error);
process.exit(1);
});
}