File size: 4,676 Bytes
da2e594
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/**
 * Protocol Version Negotiation Utility
 * 
 * Handles MCP protocol version negotiation between server and clients,
 * with special handling for n8n clients that require specific versions.
 */

export interface ClientInfo {
  name?: string;
  version?: string;
  [key: string]: any;
}

export interface ProtocolNegotiationResult {
  version: string;
  isN8nClient: boolean;
  reasoning: string;
}

/**
 * Standard MCP protocol version (latest)
 */
export const STANDARD_PROTOCOL_VERSION = '2025-03-26';

/**
 * n8n specific protocol version (what n8n expects)
 */
export const N8N_PROTOCOL_VERSION = '2024-11-05';

/**
 * Supported protocol versions in order of preference
 */
export const SUPPORTED_VERSIONS = [
  STANDARD_PROTOCOL_VERSION,
  N8N_PROTOCOL_VERSION,
  '2024-06-25', // Older fallback
];

/**
 * Detect if the client is n8n based on various indicators
 */
export function isN8nClient(
  clientInfo?: ClientInfo, 
  userAgent?: string,
  headers?: Record<string, string | string[] | undefined>
): boolean {
  // Check client info
  if (clientInfo?.name) {
    const clientName = clientInfo.name.toLowerCase();
    if (clientName.includes('n8n') || clientName.includes('langchain')) {
      return true;
    }
  }

  // Check user agent
  if (userAgent) {
    const ua = userAgent.toLowerCase();
    if (ua.includes('n8n') || ua.includes('langchain')) {
      return true;
    }
  }

  // Check headers for n8n-specific indicators
  if (headers) {
    // Check for n8n-specific headers or values
    const headerValues = Object.values(headers).join(' ').toLowerCase();
    if (headerValues.includes('n8n') || headerValues.includes('langchain')) {
      return true;
    }
    
    // Check specific header patterns that n8n might use
    if (headers['x-n8n-version'] || headers['x-langchain-version']) {
      return true;
    }
  }

  // Check environment variable that might indicate n8n mode
  if (process.env.N8N_MODE === 'true') {
    return true;
  }

  return false;
}

/**
 * Negotiate protocol version based on client information
 */
export function negotiateProtocolVersion(
  clientRequestedVersion?: string,
  clientInfo?: ClientInfo,
  userAgent?: string,
  headers?: Record<string, string | string[] | undefined>
): ProtocolNegotiationResult {
  const isN8n = isN8nClient(clientInfo, userAgent, headers);
  
  // For n8n clients, always use the n8n-specific version
  if (isN8n) {
    return {
      version: N8N_PROTOCOL_VERSION,
      isN8nClient: true,
      reasoning: 'n8n client detected, using n8n-compatible protocol version'
    };
  }

  // If client requested a specific version, try to honor it if supported
  if (clientRequestedVersion && SUPPORTED_VERSIONS.includes(clientRequestedVersion)) {
    return {
      version: clientRequestedVersion,
      isN8nClient: false,
      reasoning: `Using client-requested version: ${clientRequestedVersion}`
    };
  }

  // If client requested an unsupported version, use the closest supported one
  if (clientRequestedVersion) {
    // For now, default to standard version for unknown requests
    return {
      version: STANDARD_PROTOCOL_VERSION,
      isN8nClient: false,
      reasoning: `Client requested unsupported version ${clientRequestedVersion}, using standard version`
    };
  }

  // Default to standard protocol version for unknown clients
  return {
    version: STANDARD_PROTOCOL_VERSION,
    isN8nClient: false,
    reasoning: 'No specific client detected, using standard protocol version'
  };
}

/**
 * Check if a protocol version is supported
 */
export function isVersionSupported(version: string): boolean {
  return SUPPORTED_VERSIONS.includes(version);
}

/**
 * Get the most appropriate protocol version for backwards compatibility
 * This is used when we need to maintain compatibility with older clients
 */
export function getCompatibleVersion(targetVersion?: string): string {
  if (!targetVersion) {
    return STANDARD_PROTOCOL_VERSION;
  }

  if (SUPPORTED_VERSIONS.includes(targetVersion)) {
    return targetVersion;
  }

  // If not supported, return the most recent supported version
  return STANDARD_PROTOCOL_VERSION;
}

/**
 * Log protocol version negotiation for debugging
 */
export function logProtocolNegotiation(
  result: ProtocolNegotiationResult,
  logger: any,
  context?: string
): void {
  const logContext = context ? `[${context}] ` : '';
  
  logger.info(`${logContext}Protocol version negotiated`, {
    version: result.version,
    isN8nClient: result.isN8nClient,
    reasoning: result.reasoning
  });
  
  if (result.isN8nClient) {
    logger.info(`${logContext}Using n8n-compatible protocol version for better integration`);
  }
}