Skip to main content
Glama

Codex MCP Server

by cexll
workingDirResolver.ts6.63 kB
import { existsSync, statSync } from 'fs'; import { dirname, resolve, isAbsolute } from 'path'; import { Logger } from './logger.js'; /** * Project marker files that indicate the root of a project directory */ const PROJECT_MARKERS = [ 'package.json', // Node.js/JavaScript '.git', // Git repository 'pyproject.toml', // Python 'Cargo.toml', // Rust 'go.mod', // Go 'pom.xml', // Java (Maven) 'build.gradle', // Java (Gradle) 'composer.json', // PHP ] as const; /** * Maximum levels to walk up the directory tree when searching for project root */ const MAX_WALK_UP_LEVELS = 10; /** * Find the project root directory by walking up the directory tree * looking for common project marker files. * * @param startPath - The directory or file path to start searching from * @returns The project root directory, or the starting directory if no markers found */ export function findProjectRoot(startPath: string): string { try { // Ensure we start from a directory, not a file let currentDir = ensureDirectory(startPath); if (!currentDir) { Logger.debug(`findProjectRoot: Invalid start path: ${startPath}`); return startPath; } // Walk up the directory tree looking for project markers let levelsWalked = 0; while (levelsWalked < MAX_WALK_UP_LEVELS) { Logger.debug(`Checking for project markers in: ${currentDir}`); // Check if any project marker exists in current directory for (const marker of PROJECT_MARKERS) { const markerPath = resolve(currentDir, marker); if (existsSync(markerPath)) { Logger.debug(`Found project root at: ${currentDir} (marker: ${marker})`); return currentDir; } } // Move up one directory const parentDir = dirname(currentDir); // Stop if we've reached the root directory if (parentDir === currentDir) { Logger.debug(`Reached filesystem root, using: ${currentDir}`); break; } currentDir = parentDir; levelsWalked++; } // If no markers found, return the starting directory Logger.debug(`No project markers found, using start directory: ${currentDir}`); return ensureDirectory(startPath) || startPath; } catch (error) { Logger.debug(`Error in findProjectRoot: ${error}`); return startPath; } } /** * Ensures that the provided path points to a directory. * If the path is a file, returns its parent directory. * If the path doesn't exist or is invalid, returns undefined. * * @param path - The file or directory path * @returns The directory path, or undefined if invalid */ export function ensureDirectory(path?: string): string | undefined { if (!path) { return undefined; } try { // Check if path exists if (!existsSync(path)) { Logger.debug(`Path does not exist: ${path}`); return undefined; } // Get file stats const stats = statSync(path); // If it's a directory, return as-is if (stats.isDirectory()) { return path; } // If it's a file, return its parent directory if (stats.isFile()) { const parentDir = dirname(path); Logger.debug(`Path is a file, using parent directory: ${parentDir}`); return parentDir; } // If it's neither file nor directory (symlink, etc.), try to resolve Logger.debug(`Path is neither file nor directory: ${path}`); return undefined; } catch (error) { Logger.debug(`Error in ensureDirectory: ${error}`); return undefined; } } /** * Extract absolute file paths from @path syntax in the prompt. * Supports both quoted and unquoted paths. * * Examples: * - @/absolute/path/to/file.ts * - @"/path with spaces/file.ts" * - @'/path with spaces/file.ts' * * @param prompt - The user prompt that may contain @path references * @returns Array of absolute paths found in the prompt */ export function extractPathFromAtSyntax(prompt: string): string[] { const paths: string[] = []; // Pattern 1: Quoted paths with @ prefix: @"path" or @'path' const quotedPathRegex = /@["']([^"']+)["']/g; let match; while ((match = quotedPathRegex.exec(prompt)) !== null) { const path = match[1]; if (isAbsolute(path)) { paths.push(path); } } // Pattern 2: Unquoted paths with @ prefix: @/path (no spaces) const unquotedPathRegex = /@(\/[^\s"']+)/g; while ((match = unquotedPathRegex.exec(prompt)) !== null) { const path = match[1]; if (isAbsolute(path)) { paths.push(path); } } Logger.debug(`Extracted ${paths.length} paths from @syntax: ${paths.join(', ')}`); return paths; } /** * Resolve the working directory using a fallback chain with multiple strategies. * * Priority order (highest to lowest): * 1. Explicit workingDir parameter * 2. Environment variables: CODEX_MCP_CWD > PWD > INIT_CWD * 3. Automatic inference from @path syntax in prompt * 4. process.cwd() as last resort * * @param options - Resolution options * @returns The resolved working directory path */ export function resolveWorkingDirectory(options?: { workingDir?: string; prompt?: string; }): string | undefined { const { workingDir, prompt } = options || {}; // Priority 1: Explicit workingDir parameter if (workingDir) { const validDir = ensureDirectory(workingDir); if (validDir) { Logger.debug(`Using explicit working directory: ${validDir}`); return validDir; } else { Logger.warn(`Explicit workingDir is invalid: ${workingDir}`); } } // Priority 2: Environment variables const envVars = ['CODEX_MCP_CWD', 'PWD', 'INIT_CWD'] as const; for (const envVar of envVars) { const envValue = process.env[envVar]; if (envValue) { const validDir = ensureDirectory(envValue); if (validDir) { Logger.debug(`Using environment variable ${envVar}: ${validDir}`); return validDir; } else { Logger.debug(`Environment variable ${envVar} is invalid: ${envValue}`); } } } // Priority 3: Automatic inference from @path syntax if (prompt) { const paths = extractPathFromAtSyntax(prompt); for (const path of paths) { if (existsSync(path)) { // Find the project root for this path const projectRoot = findProjectRoot(path); if (projectRoot) { Logger.debug(`Inferred working directory from @path syntax: ${projectRoot}`); return projectRoot; } } } } // Priority 4: process.cwd() as fallback const cwd = process.cwd(); Logger.debug(`Using process.cwd() as working directory: ${cwd}`); return cwd; }

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/cexll/codex-mcp-server'

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