lokalise-mcp
Version:
The Lokalise MCP Server brings Lokalise's localization power to Claude and AI assistants—manage projects, keys, and translations by chat.
159 lines (158 loc) • 6.29 kB
JavaScript
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import dotenv from "dotenv";
import { Logger } from "./logger.util.js";
/**
* Configuration loader that handles multiple sources with priority:
* 1. Direct ENV pass (process.env)
* 2. .env file in project root
* 3. Global config file at $HOME/.mcp/configs.json
*/
class ConfigLoader {
/**
* Create a new ConfigLoader instance
* @param packageName The package name to use for global config lookup
*/
constructor(packageName) {
this.configLoaded = false;
this.packageName = packageName;
}
/**
* Load configuration from all sources with proper priority
*/
load() {
const methodLogger = Logger.forContext("utils/config.util.ts", "load");
if (this.configLoaded) {
methodLogger.debug("Configuration already loaded, skipping");
return;
}
methodLogger.debug("Loading configuration...");
// Priority 3: Load from global config file
this.loadFromGlobalConfig();
// Priority 2: Load from .env file
this.loadFromEnvFile();
// Priority 1: Direct ENV pass is already in process.env
// No need to do anything as it already has highest priority
this.configLoaded = true;
methodLogger.debug("Configuration loaded successfully");
}
/**
* Load configuration from .env file in project root
*/
loadFromEnvFile() {
const methodLogger = Logger.forContext("utils/config.util.ts", "loadFromEnvFile");
try {
const result = dotenv.config();
if (result.error) {
methodLogger.debug("No .env file found or error reading it");
return;
}
methodLogger.debug("Loaded configuration from .env file");
}
catch (error) {
methodLogger.error("Error loading .env file", error);
}
}
/**
* Load configuration from global config file at $HOME/.mcp/configs.json
*/
loadFromGlobalConfig() {
const methodLogger = Logger.forContext("utils/config.util.ts", "loadFromGlobalConfig");
try {
const homedir = os.homedir();
const globalConfigPath = path.join(homedir, ".mcp", "configs.json");
if (!fs.existsSync(globalConfigPath)) {
methodLogger.debug("Global config file not found");
return;
}
const configContent = fs.readFileSync(globalConfigPath, "utf8");
const config = JSON.parse(configContent);
// Determine the potential keys for the current package
const shortKey = "boilerplate"; // Project-specific short key
const fullPackageName = this.packageName; // e.g., '@aashari/boilerplate-mcp-server'
const unscopedPackageName = fullPackageName.split("/")[1] || fullPackageName; // e.g., 'boilerplate-mcp-server'
const potentialKeys = [shortKey, fullPackageName, unscopedPackageName];
let foundConfigSection = null;
let usedKey = null;
for (const key of potentialKeys) {
if (config[key] &&
typeof config[key] === "object" &&
config[key].environments) {
foundConfigSection = config[key];
usedKey = key;
methodLogger.debug(`Found configuration using key: ${key}`);
break; // Stop once found
}
}
if (!foundConfigSection || !foundConfigSection.environments) {
methodLogger.debug(`No configuration found for ${this.packageName} using keys: ${potentialKeys.join(", ")}`);
return;
}
const environments = foundConfigSection.environments;
for (const [key, value] of Object.entries(environments)) {
// Only set if not already defined in process.env
if (process.env[key] === undefined) {
process.env[key] = String(value);
}
}
methodLogger.debug(`Loaded configuration from global config file using key: ${usedKey}`);
}
catch (error) {
methodLogger.error("Error loading global config file", error);
}
}
/**
* Get a configuration value
* @param key The configuration key
* @param defaultValue The default value if the key is not found
* @returns The configuration value or the default value
*/
get(key, defaultValue) {
return process.env[key] || defaultValue;
}
/**
* Get the Lokalise hostname from the configuration
* @param defaultValue The default value if the key is not found
* @returns The Lokalise hostname or the default value
*/
getLokaliseHostname(defaultValue) {
const value = this.get("LOKALISE_API_HOSTNAME");
if (value === undefined) {
return defaultValue;
}
try {
const url = new URL(value);
const hostname = url.hostname;
const parts = hostname.split(".");
// If we have more than 2 parts, include the first subdomain
// api.stage.lokalise.cloud → stage.lokalise.cloud
// api.lokalise.com → lokalise.com
if (parts.length > 2) {
return parts.slice(-3).join(".");
}
if (parts.length >= 2) {
return parts.slice(-2).join(".");
}
return hostname;
}
catch {
return defaultValue;
}
}
/**
* Get a boolean configuration value
* @param key The configuration key
* @param defaultValue The default value if the key is not found
* @returns The boolean configuration value or the default value
*/
getBoolean(key, defaultValue = false) {
const value = this.get(key);
if (value === undefined) {
return defaultValue;
}
return value.toLowerCase() === "true";
}
}
// Create and export a singleton instance with the package name from package.json
export const config = new ConfigLoader("@aashari/boilerplate-mcp-server");