@dollhousemcp/mcp-server
Version:
DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.
102 lines • 12.5 kB
JavaScript
/**
* Shared environment variable parsing utilities
*
* Used by active-element-limits.ts, autonomy-config.ts,
* and ensembles/constants.ts to avoid duplicating env-var parsing logic.
*/
import { logger } from '../utils/logger.js';
/**
* Parse an integer from environment variable with validation and clamping.
*
* - Returns defaultValue if env var is unset or empty
* - Returns defaultValue with warning if value is non-numeric
* - Clamps to min with warning if value is below safety floor
* - Clamps to max with warning if value exceeds security ceiling
*
* @param envVar - Environment variable name
* @param defaultValue - Default value if not set or invalid
* @param min - Minimum allowed value (safety floor)
* @param max - Maximum allowed value (security ceiling)
* @param domain - Domain label for warning messages (e.g. 'Active element limit', 'Ensemble limit')
* @returns Validated integer value
*/
export function parseEnvInt(envVar, defaultValue, min, max, domain = 'Configuration') {
const envValue = process.env[envVar];
if (envValue === undefined || envValue === '') {
return defaultValue;
}
const parsed = parseInt(envValue, 10);
if (isNaN(parsed)) {
logger.warn(`${domain} environment variable validation failed: non-numeric value`, {
envVar,
providedValue: envValue,
expectedType: 'integer',
validRange: `${min}-${max}`,
defaultValue,
resolution: `Using default value: ${defaultValue}`,
suggestion: `Set ${envVar} to an integer between ${min} and ${max}`
});
return defaultValue;
}
if (parsed < min) {
logger.warn(`${domain} environment variable validation failed: value below minimum`, {
envVar,
providedValue: parsed,
minimumAllowed: min,
maximumAllowed: max,
validRange: `${min}-${max}`,
resolution: `Clamping to minimum value: ${min}`,
suggestion: `Set ${envVar} to a value between ${min} and ${max}`
});
return min;
}
if (parsed > max) {
logger.warn(`${domain} environment variable validation failed: value exceeds security ceiling`, {
envVar,
providedValue: parsed,
minimumAllowed: min,
maximumAllowed: max,
validRange: `${min}-${max}`,
resolution: `Clamping to security ceiling: ${max}`,
reason: 'Values above the security ceiling could enable resource exhaustion',
suggestion: `Set ${envVar} to a value between ${min} and ${max}`
});
return max;
}
return parsed;
}
/**
* Parse a string enum from environment variable with validation.
*
* - Returns defaultValue if env var is unset or empty
* - Normalizes to lowercase before comparison
* - Returns defaultValue with warning if value is not in the valid set
*
* @param envVar - Environment variable name
* @param defaultValue - Default value if not set or invalid
* @param validValues - Set of allowed values (compared case-insensitively)
* @param domain - Domain label for warning messages (e.g. 'Naming validation')
* @returns Validated string value from the valid set
*/
export function parseEnvEnum(envVar, defaultValue, validValues, domain = 'Configuration') {
const envValue = process.env[envVar];
if (envValue === undefined || envValue === '') {
return defaultValue;
}
const normalized = envValue.toLowerCase();
if (validValues.has(normalized)) {
return normalized;
}
const validList = Array.from(validValues).join(', ');
logger.warn(`${domain} environment variable validation failed: invalid value`, {
envVar,
providedValue: envValue,
expectedType: 'string enum',
validValues: validList,
defaultValue,
resolution: `Using default value: ${defaultValue}`,
suggestion: `Set ${envVar} to one of: ${validList}`
});
return defaultValue;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW52LXV0aWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbmZpZy9lbnYtdXRpbHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7O0dBS0c7QUFFSCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFNUM7Ozs7Ozs7Ozs7Ozs7O0dBY0c7QUFDSCxNQUFNLFVBQVUsV0FBVyxDQUN6QixNQUFjLEVBQ2QsWUFBb0IsRUFDcEIsR0FBVyxFQUNYLEdBQVcsRUFDWCxTQUFpQixlQUFlO0lBRWhDLE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDckMsSUFBSSxRQUFRLEtBQUssU0FBUyxJQUFJLFFBQVEsS0FBSyxFQUFFLEVBQUUsQ0FBQztRQUM5QyxPQUFPLFlBQVksQ0FBQztJQUN0QixDQUFDO0lBRUQsTUFBTSxNQUFNLEdBQUcsUUFBUSxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUN0QyxJQUFJLEtBQUssQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1FBQ2xCLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxNQUFNLDREQUE0RCxFQUFFO1lBQ2pGLE1BQU07WUFDTixhQUFhLEVBQUUsUUFBUTtZQUN2QixZQUFZLEVBQUUsU0FBUztZQUN2QixVQUFVLEVBQUUsR0FBRyxHQUFHLElBQUksR0FBRyxFQUFFO1lBQzNCLFlBQVk7WUFDWixVQUFVLEVBQUUsd0JBQXdCLFlBQVksRUFBRTtZQUNsRCxVQUFVLEVBQUUsT0FBTyxNQUFNLDBCQUEwQixHQUFHLFFBQVEsR0FBRyxFQUFFO1NBQ3BFLENBQUMsQ0FBQztRQUNILE9BQU8sWUFBWSxDQUFDO0lBQ3RCLENBQUM7SUFFRCxJQUFJLE1BQU0sR0FBRyxHQUFHLEVBQUUsQ0FBQztRQUNqQixNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsTUFBTSw4REFBOEQsRUFBRTtZQUNuRixNQUFNO1lBQ04sYUFBYSxFQUFFLE1BQU07WUFDckIsY0FBYyxFQUFFLEdBQUc7WUFDbkIsY0FBYyxFQUFFLEdBQUc7WUFDbkIsVUFBVSxFQUFFLEdBQUcsR0FBRyxJQUFJLEdBQUcsRUFBRTtZQUMzQixVQUFVLEVBQUUsOEJBQThCLEdBQUcsRUFBRTtZQUMvQyxVQUFVLEVBQUUsT0FBTyxNQUFNLHVCQUF1QixHQUFHLFFBQVEsR0FBRyxFQUFFO1NBQ2pFLENBQUMsQ0FBQztRQUNILE9BQU8sR0FBRyxDQUFDO0lBQ2IsQ0FBQztJQUVELElBQUksTUFBTSxHQUFHLEdBQUcsRUFBRSxDQUFDO1FBQ2pCLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxNQUFNLHlFQUF5RSxFQUFFO1lBQzlGLE1BQU07WUFDTixhQUFhLEVBQUUsTUFBTTtZQUNyQixjQUFjLEVBQUUsR0FBRztZQUNuQixjQUFjLEVBQUUsR0FBRztZQUNuQixVQUFVLEVBQUUsR0FBRyxHQUFHLElBQUksR0FBRyxFQUFFO1lBQzNCLFVBQVUsRUFBRSxpQ0FBaUMsR0FBRyxFQUFFO1lBQ2xELE1BQU0sRUFBRSxvRUFBb0U7WUFDNUUsVUFBVSxFQUFFLE9BQU8sTUFBTSx1QkFBdUIsR0FBRyxRQUFRLEdBQUcsRUFBRTtTQUNqRSxDQUFDLENBQUM7UUFDSCxPQUFPLEdBQUcsQ0FBQztJQUNiLENBQUM7SUFFRCxPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDO0FBRUQ7Ozs7Ozs7Ozs7OztHQVlHO0FBQ0gsTUFBTSxVQUFVLFlBQVksQ0FDMUIsTUFBYyxFQUNkLFlBQWUsRUFDZixXQUFnQyxFQUNoQyxTQUFpQixlQUFlO0lBRWhDLE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDckMsSUFBSSxRQUFRLEtBQUssU0FBUyxJQUFJLFFBQVEsS0FBSyxFQUFFLEVBQUUsQ0FBQztRQUM5QyxPQUFPLFlBQVksQ0FBQztJQUN0QixDQUFDO0lBRUQsTUFBTSxVQUFVLEdBQUcsUUFBUSxDQUFDLFdBQVcsRUFBRSxDQUFDO0lBQzFDLElBQUksV0FBVyxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO1FBQ2hDLE9BQU8sVUFBZSxDQUFDO0lBQ3pCLENBQUM7SUFFRCxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNyRCxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsTUFBTSx3REFBd0QsRUFBRTtRQUM3RSxNQUFNO1FBQ04sYUFBYSxFQUFFLFFBQVE7UUFDdkIsWUFBWSxFQUFFLGFBQWE7UUFDM0IsV0FBVyxFQUFFLFNBQVM7UUFDdEIsWUFBWTtRQUNaLFVBQVUsRUFBRSx3QkFBd0IsWUFBWSxFQUFFO1FBQ2xELFVBQVUsRUFBRSxPQUFPLE1BQU0sZUFBZSxTQUFTLEVBQUU7S0FDcEQsQ0FBQyxDQUFDO0lBQ0gsT0FBTyxZQUFZLENBQUM7QUFDdEIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU2hhcmVkIGVudmlyb25tZW50IHZhcmlhYmxlIHBhcnNpbmcgdXRpbGl0aWVzXG4gKlxuICogVXNlZCBieSBhY3RpdmUtZWxlbWVudC1saW1pdHMudHMsIGF1dG9ub215LWNvbmZpZy50cyxcbiAqIGFuZCBlbnNlbWJsZXMvY29uc3RhbnRzLnRzIHRvIGF2b2lkIGR1cGxpY2F0aW5nIGVudi12YXIgcGFyc2luZyBsb2dpYy5cbiAqL1xuXG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi91dGlscy9sb2dnZXIuanMnO1xuXG4vKipcbiAqIFBhcnNlIGFuIGludGVnZXIgZnJvbSBlbnZpcm9ubWVudCB2YXJpYWJsZSB3aXRoIHZhbGlkYXRpb24gYW5kIGNsYW1waW5nLlxuICpcbiAqIC0gUmV0dXJucyBkZWZhdWx0VmFsdWUgaWYgZW52IHZhciBpcyB1bnNldCBvciBlbXB0eVxuICogLSBSZXR1cm5zIGRlZmF1bHRWYWx1ZSB3aXRoIHdhcm5pbmcgaWYgdmFsdWUgaXMgbm9uLW51bWVyaWNcbiAqIC0gQ2xhbXBzIHRvIG1pbiB3aXRoIHdhcm5pbmcgaWYgdmFsdWUgaXMgYmVsb3cgc2FmZXR5IGZsb29yXG4gKiAtIENsYW1wcyB0byBtYXggd2l0aCB3YXJuaW5nIGlmIHZhbHVlIGV4Y2VlZHMgc2VjdXJpdHkgY2VpbGluZ1xuICpcbiAqIEBwYXJhbSBlbnZWYXIgLSBFbnZpcm9ubWVudCB2YXJpYWJsZSBuYW1lXG4gKiBAcGFyYW0gZGVmYXVsdFZhbHVlIC0gRGVmYXVsdCB2YWx1ZSBpZiBub3Qgc2V0IG9yIGludmFsaWRcbiAqIEBwYXJhbSBtaW4gLSBNaW5pbXVtIGFsbG93ZWQgdmFsdWUgKHNhZmV0eSBmbG9vcilcbiAqIEBwYXJhbSBtYXggLSBNYXhpbXVtIGFsbG93ZWQgdmFsdWUgKHNlY3VyaXR5IGNlaWxpbmcpXG4gKiBAcGFyYW0gZG9tYWluIC0gRG9tYWluIGxhYmVsIGZvciB3YXJuaW5nIG1lc3NhZ2VzIChlLmcuICdBY3RpdmUgZWxlbWVudCBsaW1pdCcsICdFbnNlbWJsZSBsaW1pdCcpXG4gKiBAcmV0dXJucyBWYWxpZGF0ZWQgaW50ZWdlciB2YWx1ZVxuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VFbnZJbnQoXG4gIGVudlZhcjogc3RyaW5nLFxuICBkZWZhdWx0VmFsdWU6IG51bWJlcixcbiAgbWluOiBudW1iZXIsXG4gIG1heDogbnVtYmVyLFxuICBkb21haW46IHN0cmluZyA9ICdDb25maWd1cmF0aW9uJ1xuKTogbnVtYmVyIHtcbiAgY29uc3QgZW52VmFsdWUgPSBwcm9jZXNzLmVudltlbnZWYXJdO1xuICBpZiAoZW52VmFsdWUgPT09IHVuZGVmaW5lZCB8fCBlbnZWYWx1ZSA9PT0gJycpIHtcbiAgICByZXR1cm4gZGVmYXVsdFZhbHVlO1xuICB9XG5cbiAgY29uc3QgcGFyc2VkID0gcGFyc2VJbnQoZW52VmFsdWUsIDEwKTtcbiAgaWYgKGlzTmFOKHBhcnNlZCkpIHtcbiAgICBsb2dnZXIud2FybihgJHtkb21haW59IGVudmlyb25tZW50IHZhcmlhYmxlIHZhbGlkYXRpb24gZmFpbGVkOiBub24tbnVtZXJpYyB2YWx1ZWAsIHtcbiAgICAgIGVudlZhcixcbiAgICAgIHByb3ZpZGVkVmFsdWU6IGVudlZhbHVlLFxuICAgICAgZXhwZWN0ZWRUeXBlOiAnaW50ZWdlcicsXG4gICAgICB2YWxpZFJhbmdlOiBgJHttaW59LSR7bWF4fWAsXG4gICAgICBkZWZhdWx0VmFsdWUsXG4gICAgICByZXNvbHV0aW9uOiBgVXNpbmcgZGVmYXVsdCB2YWx1ZTogJHtkZWZhdWx0VmFsdWV9YCxcbiAgICAgIHN1Z2dlc3Rpb246IGBTZXQgJHtlbnZWYXJ9IHRvIGFuIGludGVnZXIgYmV0d2VlbiAke21pbn0gYW5kICR7bWF4fWBcbiAgICB9KTtcbiAgICByZXR1cm4gZGVmYXVsdFZhbHVlO1xuICB9XG5cbiAgaWYgKHBhcnNlZCA8IG1pbikge1xuICAgIGxvZ2dlci53YXJuKGAke2RvbWFpbn0gZW52aXJvbm1lbnQgdmFyaWFibGUgdmFsaWRhdGlvbiBmYWlsZWQ6IHZhbHVlIGJlbG93IG1pbmltdW1gLCB7XG4gICAgICBlbnZWYXIsXG4gICAgICBwcm92aWRlZFZhbHVlOiBwYXJzZWQsXG4gICAgICBtaW5pbXVtQWxsb3dlZDogbWluLFxuICAgICAgbWF4aW11bUFsbG93ZWQ6IG1heCxcbiAgICAgIHZhbGlkUmFuZ2U6IGAke21pbn0tJHttYXh9YCxcbiAgICAgIHJlc29sdXRpb246IGBDbGFtcGluZyB0byBtaW5pbXVtIHZhbHVlOiAke21pbn1gLFxuICAgICAgc3VnZ2VzdGlvbjogYFNldCAke2VudlZhcn0gdG8gYSB2YWx1ZSBiZXR3ZWVuICR7bWlufSBhbmQgJHttYXh9YFxuICAgIH0pO1xuICAgIHJldHVybiBtaW47XG4gIH1cblxuICBpZiAocGFyc2VkID4gbWF4KSB7XG4gICAgbG9nZ2VyLndhcm4oYCR7ZG9tYWlufSBlbnZpcm9ubWVudCB2YXJpYWJsZSB2YWxpZGF0aW9uIGZhaWxlZDogdmFsdWUgZXhjZWVkcyBzZWN1cml0eSBjZWlsaW5nYCwge1xuICAgICAgZW52VmFyLFxuICAgICAgcHJvdmlkZWRWYWx1ZTogcGFyc2VkLFxuICAgICAgbWluaW11bUFsbG93ZWQ6IG1pbixcbiAgICAgIG1heGltdW1BbGxvd2VkOiBtYXgsXG4gICAgICB2YWxpZFJhbmdlOiBgJHttaW59LSR7bWF4fWAsXG4gICAgICByZXNvbHV0aW9uOiBgQ2xhbXBpbmcgdG8gc2VjdXJpdHkgY2VpbGluZzogJHttYXh9YCxcbiAgICAgIHJlYXNvbjogJ1ZhbHVlcyBhYm92ZSB0aGUgc2VjdXJpdHkgY2VpbGluZyBjb3VsZCBlbmFibGUgcmVzb3VyY2UgZXhoYXVzdGlvbicsXG4gICAgICBzdWdnZXN0aW9uOiBgU2V0ICR7ZW52VmFyfSB0byBhIHZhbHVlIGJldHdlZW4gJHttaW59IGFuZCAke21heH1gXG4gICAgfSk7XG4gICAgcmV0dXJuIG1heDtcbiAgfVxuXG4gIHJldHVybiBwYXJzZWQ7XG59XG5cbi8qKlxuICogUGFyc2UgYSBzdHJpbmcgZW51bSBmcm9tIGVudmlyb25tZW50IHZhcmlhYmxlIHdpdGggdmFsaWRhdGlvbi5cbiAqXG4gKiAtIFJldHVybnMgZGVmYXVsdFZhbHVlIGlmIGVudiB2YXIgaXMgdW5zZXQgb3IgZW1wdHlcbiAqIC0gTm9ybWFsaXplcyB0byBsb3dlcmNhc2UgYmVmb3JlIGNvbXBhcmlzb25cbiAqIC0gUmV0dXJucyBkZWZhdWx0VmFsdWUgd2l0aCB3YXJuaW5nIGlmIHZhbHVlIGlzIG5vdCBpbiB0aGUgdmFsaWQgc2V0XG4gKlxuICogQHBhcmFtIGVudlZhciAtIEVudmlyb25tZW50IHZhcmlhYmxlIG5hbWVcbiAqIEBwYXJhbSBkZWZhdWx0VmFsdWUgLSBEZWZhdWx0IHZhbHVlIGlmIG5vdCBzZXQgb3IgaW52YWxpZFxuICogQHBhcmFtIHZhbGlkVmFsdWVzIC0gU2V0IG9mIGFsbG93ZWQgdmFsdWVzIChjb21wYXJlZCBjYXNlLWluc2Vuc2l0aXZlbHkpXG4gKiBAcGFyYW0gZG9tYWluIC0gRG9tYWluIGxhYmVsIGZvciB3YXJuaW5nIG1lc3NhZ2VzIChlLmcuICdOYW1pbmcgdmFsaWRhdGlvbicpXG4gKiBAcmV0dXJucyBWYWxpZGF0ZWQgc3RyaW5nIHZhbHVlIGZyb20gdGhlIHZhbGlkIHNldFxuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VFbnZFbnVtPFQgZXh0ZW5kcyBzdHJpbmc+KFxuICBlbnZWYXI6IHN0cmluZyxcbiAgZGVmYXVsdFZhbHVlOiBULFxuICB2YWxpZFZhbHVlczogUmVhZG9ubHlTZXQ8c3RyaW5nPixcbiAgZG9tYWluOiBzdHJpbmcgPSAnQ29uZmlndXJhdGlvbidcbik6IFQge1xuICBjb25zdCBlbnZWYWx1ZSA9IHByb2Nlc3MuZW52W2VudlZhcl07XG4gIGlmIChlbnZWYWx1ZSA9PT0gdW5kZWZpbmVkIHx8IGVudlZhbHVlID09PSAnJykge1xuICAgIHJldHVybiBkZWZhdWx0VmFsdWU7XG4gIH1cblxuICBjb25zdCBub3JtYWxpemVkID0gZW52VmFsdWUudG9Mb3dlckNhc2UoKTtcbiAgaWYgKHZhbGlkVmFsdWVzLmhhcyhub3JtYWxpemVkKSkge1xuICAgIHJldHVybiBub3JtYWxpemVkIGFzIFQ7XG4gIH1cblxuICBjb25zdCB2YWxpZExpc3QgPSBBcnJheS5mcm9tKHZhbGlkVmFsdWVzKS5qb2luKCcsICcpO1xuICBsb2dnZXIud2FybihgJHtkb21haW59IGVudmlyb25tZW50IHZhcmlhYmxlIHZhbGlkYXRpb24gZmFpbGVkOiBpbnZhbGlkIHZhbHVlYCwge1xuICAgIGVudlZhcixcbiAgICBwcm92aWRlZFZhbHVlOiBlbnZWYWx1ZSxcbiAgICBleHBlY3RlZFR5cGU6ICdzdHJpbmcgZW51bScsXG4gICAgdmFsaWRWYWx1ZXM6IHZhbGlkTGlzdCxcbiAgICBkZWZhdWx0VmFsdWUsXG4gICAgcmVzb2x1dGlvbjogYFVzaW5nIGRlZmF1bHQgdmFsdWU6ICR7ZGVmYXVsdFZhbHVlfWAsXG4gICAgc3VnZ2VzdGlvbjogYFNldCAke2VudlZhcn0gdG8gb25lIG9mOiAke3ZhbGlkTGlzdH1gXG4gIH0pO1xuICByZXR1cm4gZGVmYXVsdFZhbHVlO1xufVxuIl19