Files
redmine/redMCP/app/McpEnvironment.php
Jason Thistlethwaite 22c8e915e9 Sanitize noisy MCP text fields by default
Clean control and invisible junk from tool result text fields to reduce token waste while preserving readable Unicode. Add an MCP_TEXT_SANITIZATION toggle and regression tests for enabled and disabled behavior.
2026-05-06 02:31:25 -04:00

83 lines
2.5 KiB
PHP

<?php
declare(strict_types=1);
namespace RedMCP;
use RuntimeException;
final class McpEnvironment
{
/**
* @return array{redmine_url:string,redmine_api_key:string,mcp_server_token:?string,mcp_debug_log:?string,mcp_text_sanitization:bool}
*/
public static function load(string $envFile): array
{
$env = self::loadFile($envFile);
$apiKey = getenv('REDMINE_API_KEY') ?: getenv('REDMNINE_API_KEY') ?: ($env['REDMINE_API_KEY'] ?? $env['REDMNINE_API_KEY'] ?? null);
if (!is_string($apiKey) || trim($apiKey) === '') {
throw new RuntimeException('REDMINE_API_KEY is required in the environment or redMCP/.env');
}
return [
'redmine_url' => 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<string,string>
*/
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;
}
}