Skip to main content
Glama
paths.ts5.5 kB
/** * Path resolution for multi-mount filesystem. */ import path from 'node:path'; import { config, type Mount } from '../config/env.js'; export interface ResolvedPath { /** The mount this path belongs to */ mount: Mount; /** Absolute path on the filesystem */ absolutePath: string; /** Path relative to the mount (without mount name prefix) */ relativePath: string; /** Full virtual path (with mount name prefix) */ virtualPath: string; } export type PathResolutionResult = | { ok: true; resolved: ResolvedPath } | { ok: false; error: string }; /** * Check if a path segment attempts to escape (e.g., "..") */ function hasEscapeAttempt(pathStr: string): boolean { const segments = pathStr.split(/[/\\]/); return segments.some((seg) => seg === '..'); } /** * Check if a path is an absolute filesystem path (not a virtual mount path). */ function isAbsolutePath(pathStr: string): boolean { // Unix absolute path: starts with / if (pathStr.startsWith('/')) { return true; } // Windows absolute path: starts with drive letter (C:\, D:\, etc.) if (/^[a-zA-Z]:[/\\]/.test(pathStr)) { return true; } return false; } /** * Resolve a virtual path to a real filesystem path. * * Virtual paths are structured as: mountName/path/to/file * For example: "vault/notes/todo.md" resolves to the vault mount + "notes/todo.md" * * Special case: "." returns null mount, indicating root listing should show mounts. */ export function resolvePath(virtualPath: string): PathResolutionResult { const trimmed = virtualPath.trim(); // Check for absolute paths (not allowed - this is a sandboxed filesystem) if (isAbsolutePath(trimmed)) { const mounts = config.MOUNTS.map((m) => m.name).join(', '); return { ok: false, error: `SANDBOXED FILESYSTEM: Absolute paths like "${trimmed}" are not allowed. ` + `This tool only accesses specific mounted directories. ` + `Available mounts: ${mounts}. Use fs_read(".") to explore.`, }; } // Check for escape attempts if (hasEscapeAttempt(trimmed)) { return { ok: false, error: 'Path cannot contain ".." segments (no directory traversal)' }; } // Special case: root path shows mount listing if (trimmed === '.' || trimmed === '' || trimmed === '/') { // Return first mount as a placeholder - handler will show mount list const mount = config.MOUNTS[0]; if (!mount) { return { ok: false, error: 'No filesystem mounts configured' }; } return { ok: true, resolved: { mount, absolutePath: mount.absolutePath, relativePath: '.', virtualPath: '.', }, }; } // Normalize path separators const normalized = trimmed.replace(/\\/g, '/').replace(/^\/+/, ''); // Extract mount name (first segment) const segments = normalized.split('/'); const mountName = segments[0]; const restPath = segments.slice(1).join('/') || '.'; // Find matching mount const mount = config.MOUNTS.find((m) => m.name === mountName); if (!mount) { // Maybe user is trying to access without mount prefix? // If there's only one mount, be lenient if (config.MOUNTS.length === 1) { const singleMount = config.MOUNTS[0]; if (!singleMount) { return { ok: false, error: 'No filesystem mounts configured' }; } const absolutePath = path.resolve(singleMount.absolutePath, normalized); // Security check: ensure path is within mount if ( !absolutePath.startsWith(singleMount.absolutePath + path.sep) && absolutePath !== singleMount.absolutePath ) { return { ok: false, error: 'Path is outside allowed directory' }; } return { ok: true, resolved: { mount: singleMount, absolutePath, relativePath: normalized, virtualPath: normalized, }, }; } const availableMounts = config.MOUNTS.map((m) => `"${m.name}/"`).join(', '); return { ok: false, error: `Path "${normalized}" does not match any mount. Available mounts: ${availableMounts}. ` + `Paths must start with a mount name (e.g., "${config.MOUNTS[0]?.name}/file.md").`, }; } // Resolve the path within the mount const absolutePath = path.resolve(mount.absolutePath, restPath); // Security check: ensure path is within mount if ( !absolutePath.startsWith(mount.absolutePath + path.sep) && absolutePath !== mount.absolutePath ) { return { ok: false, error: 'Path is outside allowed directory' }; } return { ok: true, resolved: { mount, absolutePath, relativePath: restPath, virtualPath: restPath === '.' ? mount.name : `${mount.name}/${restPath}`, }, }; } /** * Convert an absolute path back to a virtual path. */ export function toVirtualPath(absolutePath: string): string | null { for (const mount of config.MOUNTS) { if (absolutePath === mount.absolutePath) { return mount.name; } if (absolutePath.startsWith(mount.absolutePath + path.sep)) { const relative = absolutePath.slice(mount.absolutePath.length + 1); return `${mount.name}/${relative}`; } } return null; } /** * Get all configured mounts. */ export function getMounts(): Mount[] { return config.MOUNTS; } /** * Check if we're in single-mount mode (backward compatible). */ export function isSingleMount(): boolean { return config.MOUNTS.length === 1; }

Latest Blog Posts

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/iceener/files-stdio-mcp-server'

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