rtrim((string) (getenv('REDMINE_URL') ?: ($env['REDMINE_URL'] ?? 'http://192.168.50.170')), '/'), 'redmine_api_key' => $apiKey, 'mcp_server_token' => self::optionalString(getenv('MCP_SERVER_TOKEN') ?: ($env['MCP_SERVER_TOKEN'] ?? null)), 'mcp_debug_log' => self::optionalString(getenv('MCP_DEBUG_LOG') ?: ($env['MCP_DEBUG_LOG'] ?? null)), 'mcp_text_sanitization' => self::boolSetting(getenv('MCP_TEXT_SANITIZATION') ?: ($env['MCP_TEXT_SANITIZATION'] ?? null), true), ]; } /** * @return array */ private static function loadFile(string $path): array { if (!is_file($path)) { return []; } $values = []; foreach (file($path, FILE_IGNORE_NEW_LINES) ?: [] as $line) { $line = trim($line); if ($line === '' || str_starts_with($line, '#') || !str_contains($line, '=')) { continue; } [$key, $value] = explode('=', $line, 2); $values[trim($key)] = trim(trim($value), "\"'"); } return $values; } private static function optionalString(mixed $value): ?string { if (!is_string($value) || trim($value) === '') { return null; } return $value; } private static function boolSetting(mixed $value, bool $default): bool { if (!is_string($value)) { return $default; } $normalized = strtolower(trim($value)); if ($normalized === '') { return $default; } if (in_array($normalized, ['1', 'true', 'yes', 'on'], true)) { return true; } if (in_array($normalized, ['0', 'false', 'no', 'off'], true)) { return false; } return $default; } }