From 294df1d4872eaff4c813f3d68143648c29764be8 Mon Sep 17 00:00:00 2001 From: buggyhunter <82619612+buggyhunter@users.noreply.github.com> Date: Fri, 18 Jul 2025 19:24:48 +0300 Subject: [PATCH] CTX7-272: ip address header --- src/index.ts | 16 ++++++++++------ src/lib/api.ts | 38 ++++++++++++++++++++------------------ 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/index.ts b/src/index.ts index 7d59335..6af25d4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -55,7 +55,7 @@ function getClientIp(req: IncomingMessage): string | undefined { const ips = Array.isArray(forwardedFor) ? forwardedFor[0] : forwardedFor; return ips.split(",")[0].trim(); } - + // Fall back to socket remote address return req.socket?.remoteAddress || undefined; } @@ -165,10 +165,14 @@ ${resultsText}`, ), }, async ({ context7CompatibleLibraryID, tokens = DEFAULT_MINIMUM_TOKENS, topic = "" }) => { - const fetchDocsResponse = await fetchLibraryDocumentation(context7CompatibleLibraryID, { - tokens, - topic, - }, clientIp); + const fetchDocsResponse = await fetchLibraryDocumentation( + context7CompatibleLibraryID, + { + tokens, + topic, + }, + clientIp + ); if (!fetchDocsResponse) { return { @@ -221,7 +225,7 @@ async function main() { try { // Extract client IP address using socket remote address (most reliable) const clientIp = getClientIp(req); - + // Create new server instance for each request const requestServer = createServerInstance(clientIp); diff --git a/src/lib/api.ts b/src/lib/api.ts index ffb4e16..bb84cc0 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -5,8 +5,10 @@ const CONTEXT7_API_BASE_URL = "https://context7.com/api"; const DEFAULT_TYPE = "txt"; // Encryption configuration -const ENCRYPTION_KEY = process.env.CLIENT_IP_ENCRYPTION_KEY || "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"; -const ALGORITHM = 'aes-256-cbc'; +const ENCRYPTION_KEY = + process.env.CLIENT_IP_ENCRYPTION_KEY || + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"; +const ALGORITHM = "aes-256-cbc"; // Validate encryption key function validateEncryptionKey(key: string): boolean { @@ -19,13 +21,13 @@ function encryptClientIp(clientIp: string): string { console.error("Invalid encryption key format. Must be 64 hex characters."); return clientIp; // Fallback to unencrypted } - + try { const iv = randomBytes(16); - const cipher = createCipheriv(ALGORITHM, Buffer.from(ENCRYPTION_KEY, 'hex'), iv); - let encrypted = cipher.update(clientIp, 'utf8', 'hex'); - encrypted += cipher.final('hex'); - return iv.toString('hex') + ':' + encrypted; + const cipher = createCipheriv(ALGORITHM, Buffer.from(ENCRYPTION_KEY, "hex"), iv); + let encrypted = cipher.update(clientIp, "utf8", "hex"); + encrypted += cipher.final("hex"); + return iv.toString("hex") + ":" + encrypted; } catch (error) { console.error("Error encrypting client IP:", error); return clientIp; // Fallback to unencrypted @@ -37,19 +39,19 @@ export function decryptClientIp(encryptedIp: string): string | null { console.error("Invalid encryption key format. Cannot decrypt."); return null; } - + try { - const parts = encryptedIp.split(':'); + const parts = encryptedIp.split(":"); if (parts.length !== 2) { // Not encrypted, return as-is return encryptedIp; } - - const iv = Buffer.from(parts[0], 'hex'); + + const iv = Buffer.from(parts[0], "hex"); const encrypted = parts[1]; - const decipher = createDecipheriv(ALGORITHM, Buffer.from(ENCRYPTION_KEY, 'hex'), iv); - let decrypted = decipher.update(encrypted, 'hex', 'utf8'); - decrypted += decipher.final('utf8'); + const decipher = createDecipheriv(ALGORITHM, Buffer.from(ENCRYPTION_KEY, "hex"), iv); + let decrypted = decipher.update(encrypted, "hex", "utf8"); + decrypted += decipher.final("utf8"); return decrypted; } catch (error) { console.error("Error decrypting client IP:", error); @@ -67,12 +69,12 @@ export async function searchLibraries(query: string, clientIp?: string): Promise try { const url = new URL(`${CONTEXT7_API_BASE_URL}/v1/search`); url.searchParams.set("query", query); - + const headers: Record = {}; if (clientIp) { headers["mcp-client-ip"] = encryptClientIp(clientIp); } - + const response = await fetch(url, { headers }); if (!response.ok) { const errorCode = response.status; @@ -119,14 +121,14 @@ export async function fetchLibraryDocumentation( if (options.tokens) url.searchParams.set("tokens", options.tokens.toString()); if (options.topic) url.searchParams.set("topic", options.topic); url.searchParams.set("type", DEFAULT_TYPE); - + const headers: Record = { "X-Context7-Source": "mcp-server", }; if (clientIp) { headers["mcp-client-ip"] = encryptClientIp(clientIp); } - + const response = await fetch(url, { headers }); if (!response.ok) { const errorCode = response.status;