Skip to main content
Glama

OpenFGA MCP

DebugLogger.php7.08 kB
<?php declare(strict_types=1); namespace OpenFGA\MCP; use function assert; use function date; use function dirname; use function file_put_contents; use function function_exists; use function getmypid; use function is_array; use function is_dir; use function is_string; use function json_encode; use function mkdir; final class DebugLogger { private static ?string $logDir = null; private static ?string $logFile = null; /** * @param array<string, mixed>|null $context * @param string $error * @param ?string $id */ public static function logError(string $error, ?string $id = null, ?array $context = null): void { if (! self::isDebugEnabled()) { return; } self::initializePaths(); self::ensureLogDirectory(); $entry = [ 'timestamp' => date('Y-m-d H:i:s'), 'type' => 'ERROR', 'id' => $id, 'error' => $error, 'context' => $context, ]; self::writeLog($entry); } /** * @param array<string, mixed> $params * @param string $method * @param ?string $id */ public static function logRequest(string $method, array $params, ?string $id = null): void { if (! self::isDebugEnabled()) { return; } self::initializePaths(); self::ensureLogDirectory(); $entry = [ 'timestamp' => date('Y-m-d H:i:s'), 'type' => 'REQUEST', 'id' => $id, 'method' => $method, 'params' => $params, ]; self::writeLog($entry); } /** * @param array<string, mixed> $response * @param ?string $id */ public static function logResponse(array $response, ?string $id = null): void { if (! self::isDebugEnabled()) { return; } self::initializePaths(); self::ensureLogDirectory(); $entry = [ 'timestamp' => date('Y-m-d H:i:s'), 'type' => 'RESPONSE', 'id' => $id, 'response' => $response, ]; self::writeLog($entry); } /** * @param array<string, mixed> $context * @param string $event */ public static function logServerLifecycle(string $event, array $context = []): void { if (! self::isDebugEnabled()) { return; } self::initializePaths(); self::ensureLogDirectory(); $entry = [ 'timestamp' => date('Y-m-d H:i:s'), 'type' => 'SERVER_LIFECYCLE', 'event' => $event, 'context' => $context, 'pid' => getmypid(), ]; self::writeLog($entry); } /** * @param array<int, mixed> $arguments * @param string $toolName * @param mixed $result * @param ?string $id */ public static function logToolCall(string $toolName, array $arguments, mixed $result, ?string $id = null): void { if (! self::isDebugEnabled()) { return; } self::initializePaths(); self::ensureLogDirectory(); $entry = [ 'timestamp' => date('Y-m-d H:i:s'), 'type' => 'TOOL_CALL', 'id' => $id, 'tool' => $toolName, 'arguments' => $arguments, 'result' => is_array($result) ? $result : ['value' => $result], ]; self::writeLog($entry); } private static function ensureLogDirectory(): void { assert(null !== self::$logDir, 'Log directory path must be initialized'); assert(null !== self::$logFile, 'Log file path must be initialized'); $logDir = self::$logDir; // For Psalm null-safety $logFile = self::$logFile; // For Psalm null-safety if (! is_dir($logDir) && ! mkdir($logDir, 0o755, true)) { // If we can't create the directory, log to stderr as fallback $inTestEnvironment = function_exists('test') || function_exists('it') || class_exists('PHPUnit\Framework\TestCase'); if (! $inTestEnvironment) { error_log('[MCP DEBUG] Failed to create log directory: ' . $logDir); } return; } // On first run, log where we're writing to help with debugging static $logged = false; if (! $logged) { $inTestEnvironment = function_exists('test') || function_exists('it') || class_exists('PHPUnit\Framework\TestCase'); if (! $inTestEnvironment) { error_log('[MCP DEBUG] Logging to: ' . $logFile); } $logged = true; } } /** * @param array<string, mixed> $entry */ /** * Get the project root directory by finding composer.json. */ private static function getProjectRoot(): string { static $projectRoot = null; if (null === $projectRoot) { // Start from the current file's directory and walk up to find composer.json $dir = __DIR__; while ($dir !== dirname($dir)) { if (file_exists($dir . '/composer.json')) { $projectRoot = $dir; break; } $dir = dirname($dir); } // Fallback to the directory above src if composer.json not found if (null === $projectRoot) { $projectRoot = dirname(__DIR__); } } // PHPStan doesn't realize $projectRoot is guaranteed to be set above assert(is_string($projectRoot)); return $projectRoot; } private static function initializePaths(): void { if (null === self::$logDir) { self::$logDir = self::getProjectRoot() . '/logs'; self::$logFile = self::$logDir . '/mcp-debug.log'; } } private static function isDebugEnabled(): bool { // Default to true for debugging return getConfiguredBool('OPENFGA_MCP_DEBUG', true); } /** * @param array<string, mixed> $entry */ private static function writeLog(array $entry): void { $jsonString = json_encode($entry, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); if (false === $jsonString) { $jsonString = '{"error": "Failed to encode log entry"}'; } $logLine = $jsonString . "\n"; assert(null !== self::$logFile, 'Log file path must be initialized'); $logFile = self::$logFile; $result = file_put_contents($logFile, $logLine, FILE_APPEND | LOCK_EX); if (false === $result) { $inTestEnvironment = function_exists('test') || function_exists('it') || class_exists('PHPUnit\Framework\TestCase'); if (! $inTestEnvironment) { error_log('[MCP DEBUG] Failed to write to log file: ' . $logFile); } } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/evansims/openfga-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server