From aa57e8c48bbcd905415c4b1b98ff2c365d82d448 Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Thu, 5 Mar 2026 17:55:59 +0100 Subject: [PATCH] feat(klz-payload-mcp): implement JWT authentication for robust CMS updates --- packages/klz-payload-mcp/src/index.ts | 45 +++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/packages/klz-payload-mcp/src/index.ts b/packages/klz-payload-mcp/src/index.ts index ac92502..e1a2e62 100644 --- a/packages/klz-payload-mcp/src/index.ts +++ b/packages/klz-payload-mcp/src/index.ts @@ -9,23 +9,56 @@ import { import axios from "axios"; import https from "https"; -const PAYLOAD_URL = process.env.PAYLOAD_URL || "https://klz.infra.mintel.me"; +const PAYLOAD_URL = process.env.PAYLOAD_URL || "http://localhost:3100"; const PAYLOAD_API_KEY = process.env.PAYLOAD_API_KEY; - -if (!PAYLOAD_API_KEY) { - console.error("Warning: PAYLOAD_API_KEY is not set. API calls will fail."); -} +const PAYLOAD_EMAIL = process.env.PAYLOAD_EMAIL || "agent@mintel.me"; +const PAYLOAD_PASSWORD = process.env.PAYLOAD_PASSWORD || "agentpassword123"; const httpsAgent = new https.Agent({ rejectUnauthorized: false, // For internal infra }); +let jwtToken: string | null = null; + const payloadClient = axios.create({ baseURL: `${PAYLOAD_URL}/api`, - headers: { Authorization: `users API-Key ${PAYLOAD_API_KEY}` }, + headers: PAYLOAD_API_KEY ? { Authorization: `users API-Key ${PAYLOAD_API_KEY}` } : {}, httpsAgent }); +payloadClient.interceptors.request.use(async (config) => { + if (!PAYLOAD_API_KEY && !jwtToken && PAYLOAD_EMAIL && PAYLOAD_PASSWORD) { + try { + const loginRes = await axios.post(`${PAYLOAD_URL}/api/users/login`, { + email: PAYLOAD_EMAIL, + password: PAYLOAD_PASSWORD + }, { httpsAgent }); + if (loginRes.data && loginRes.data.token) { + jwtToken = loginRes.data.token; + } + } catch (e) { + console.error("Failed to authenticate with Payload CMS using email/password."); + } + } + + if (jwtToken && !PAYLOAD_API_KEY) { + config.headers.Authorization = `JWT ${jwtToken}`; + } + return config; +}); + +payloadClient.interceptors.response.use(res => res, async (error) => { + const originalRequest = error.config; + // If token expired, clear it and retry + if (error.response?.status === 401 && !originalRequest._retry && !PAYLOAD_API_KEY) { + originalRequest._retry = true; + jwtToken = null; // Forces re-authentication on next interceptor run + return payloadClient(originalRequest); + } + return Promise.reject(error); +}); + + const SEARCH_PRODUCTS_TOOL: Tool = { name: "payload_search_products", description: "Search for technical product specifications (cables, cross-sections) in KLZ Payload CMS",