UNPKG

@grec0/mcp-s2s-asterisk

Version:

MCP server para asistente telefónico conversacional con Asterisk S2S

200 lines (199 loc) 7.29 kB
import { getUserAgent } from "universal-user-agent"; import { createPlankaError } from "./errors.js"; import { VERSION } from "./version.js"; // Global variables to store tokens let agentToken = null; async function parseResponseBody(response) { const contentType = response.headers.get("content-type"); if (contentType?.includes("application/json")) { return response.json(); } return response.text(); } export function buildUrl(baseUrl, params) { const url = new URL(baseUrl); Object.entries(params).forEach(([key, value]) => { if (value !== undefined) { url.searchParams.append(key, value.toString()); } }); return url.toString(); } const USER_AGENT = `modelcontextprotocol/servers/planka/v${VERSION} ${getUserAgent()}`; async function authenticateAgent() { const email = process.env.PLANKA_AGENT_EMAIL; const password = process.env.PLANKA_AGENT_PASSWORD; console.error(`[DEBUG] Attempting authentication with email: ${email}`); if (!email || !password) { throw new Error("PLANKA_AGENT_EMAIL and PLANKA_AGENT_PASSWORD environment variables are required"); } const baseUrl = process.env.PLANKA_BASE_URL || "http://localhost:3000"; // Construir la URL correctamente para el endpoint de tokens const url = baseUrl.endsWith('/') ? `${baseUrl}api/access-tokens` : `${baseUrl}/api/access-tokens`; console.error(`[DEBUG] Authentication URL: ${url}`); console.error(`[DEBUG] Base URL: ${baseUrl}`); try { const requestBody = JSON.stringify({ emailOrUsername: email, password: password, }); console.error(`[DEBUG] Request body: ${requestBody}`); const response = await fetch(url, { method: "POST", headers: { "Accept": "application/json", "Content-Type": "application/json", "User-Agent": USER_AGENT, }, body: requestBody, credentials: "include", }); console.error(`[DEBUG] Response status: ${response.status}`); console.error(`[DEBUG] Response headers:`, Object.fromEntries(response.headers.entries())); const responseBody = await parseResponseBody(response); console.error(`[DEBUG] Response body:`, responseBody); if (!response.ok) { throw createPlankaError(response.status, responseBody); } // The token is directly in the item field const { item } = responseBody; agentToken = item; console.error(`[DEBUG] Authentication successful, token length: ${item?.length}`); return item; } catch (error) { console.error(`[DEBUG] Authentication error:`, error); // Rethrow with more context const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to authenticate agent with Planka: ${errorMessage}`); } } async function getAuthToken() { if (agentToken) { return agentToken; } return authenticateAgent(); } export async function plankaRequest(path, options = {}) { const baseUrl = process.env.PLANKA_BASE_URL || "http://localhost:3000"; // Ensure path starts with /api/ if not already present const normalizedPath = path.startsWith("/api/") ? path : `/api/${path}`; // Construir la URL correctamente const url = baseUrl.endsWith('/') ? `${baseUrl}${normalizedPath.substring(1)}` // Remove leading slash if baseUrl ends with / : `${baseUrl}${normalizedPath}`; const headers = { "Accept": "application/json", "Content-Type": "application/json", "User-Agent": USER_AGENT, ...options.headers, }; // Remove Content-Type header for FormData if (options.body instanceof FormData) { delete headers["Content-Type"]; } // Add authentication token if not skipped if (!options.skipAuth) { try { const token = await getAuthToken(); headers["Authorization"] = `Bearer ${token}`; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to get authentication token: ${errorMessage}`); } } try { const response = await fetch(url, { method: options.method || "GET", headers, body: options.body instanceof FormData ? options.body : options.body ? JSON.stringify(options.body) : undefined, credentials: "include", // Include cookies for Planka authentication }); const responseBody = await parseResponseBody(response); if (!response.ok) { throw createPlankaError(response.status, responseBody); } return responseBody; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to make Planka request to ${url}: ${errorMessage}`); } } export function validateProjectName(name) { const sanitized = name.trim(); if (!sanitized) { throw new Error("Project name cannot be empty"); } return sanitized; } export function validateBoardName(name) { const sanitized = name.trim(); if (!sanitized) { throw new Error("Board name cannot be empty"); } return sanitized; } export function validateListName(name) { const sanitized = name.trim(); if (!sanitized) { throw new Error("List name cannot be empty"); } return sanitized; } export function validateCardName(name) { const sanitized = name.trim(); if (!sanitized) { throw new Error("Card name cannot be empty"); } return sanitized; } /** * Looks up a user ID by email * * @param {string} email - The email of the user to look up * @returns {Promise<string | null>} The user ID if found, null otherwise */ export async function getUserIdByEmail(email) { try { // Get all users const response = await plankaRequest("/api/users"); const { items } = response; // Find the user with the matching email const user = items.find((user) => user.email === email); return user ? user.id : null; } catch (error) { console.error(`Failed to get user ID by email: ${error instanceof Error ? error.message : String(error)}`); return null; } } /** * Looks up a user ID by username * * @param {string} username - The username of the user to look up * @returns {Promise<string | null>} The user ID if found, null otherwise */ export async function getUserIdByUsername(username) { try { // Get all users const response = await plankaRequest("/api/users"); const { items } = response; // Find the user with the matching username const user = items.find((user) => user.username === username); return user ? user.id : null; } catch (error) { console.error(`Failed to get user ID by username: ${error instanceof Error ? error.message : String(error)}`); return null; } }