Skip to main content
Glama

Cut-Copy-Paste Clipboard Server

path-access-control.ts•4.86 kB
import { existsSync, readFileSync } from 'fs'; import { resolve, join } from 'path'; import { minimatch } from 'minimatch'; export interface PathPattern { pattern: string; isNegation: boolean; originalLine: string; } /** * PathAccessControl manages filesystem access restrictions via an allowlist file * Supports .gitignore-style patterns with negation */ export class PathAccessControl { private patterns: PathPattern[] = []; private allowFilePath: string; constructor(allowFilePath?: string) { // Default to ~/.mcp-clipboard/paths.allow const defaultPath = join( process.env.HOME || process.env.USERPROFILE || '.', '.mcp-clipboard', 'paths.allow' ); this.allowFilePath = allowFilePath || defaultPath; this.loadPatterns(); } /** * Check if a target path is allowed by the current patterns * @param targetPath - Path to check (will be resolved to absolute) * @returns true if allowed, false if denied */ isPathAllowed(targetPath: string): boolean { const absolutePath = resolve(targetPath); // If no patterns loaded or file doesn't exist, default to allow all if (this.patterns.length === 0) { return true; } // Evaluate patterns top-to-bottom, later patterns override earlier ones let allowed = false; for (const patternObj of this.patterns) { const matches = this.matchesPattern(absolutePath, patternObj.pattern); if (matches) { // If it's a negation pattern and matches, deny access // If it's a positive pattern and matches, allow access allowed = !patternObj.isNegation; } } return allowed; } /** * Reload patterns from the allowlist file */ reload(): void { this.loadPatterns(); } /** * Get the current patterns (for testing/debugging) */ getPatterns(): PathPattern[] { return [...this.patterns]; } /** * Get the allowlist file path */ getAllowFilePath(): string { return this.allowFilePath; } /** * Load and parse patterns from the allowlist file */ private loadPatterns(): void { this.patterns = []; // If file doesn't exist, default to allow all with wildcard pattern if (!existsSync(this.allowFilePath)) { this.patterns.push({ pattern: '**/*', isNegation: false, originalLine: '*', }); return; } try { const content = readFileSync(this.allowFilePath, 'utf-8'); const lines = content.split(/\r?\n/); for (const line of lines) { const trimmed = line.trim(); // Skip empty lines and comments if (trimmed === '' || trimmed.startsWith('#')) { continue; } // Parse pattern const isNegation = trimmed.startsWith('!'); const pattern = isNegation ? trimmed.slice(1).trim() : trimmed; if (pattern === '') { continue; // Skip if pattern is empty after removing negation } // Normalize the pattern const normalizedPattern = this.normalizePattern(pattern); this.patterns.push({ pattern: normalizedPattern, isNegation, originalLine: line, }); } // If file exists but has no valid patterns, default to allow all if (this.patterns.length === 0) { this.patterns.push({ pattern: '**/*', isNegation: false, originalLine: '*', }); } } catch (error) { // If file read fails, log warning and default to allow all console.warn( `Warning: Failed to read allowlist file ${this.allowFilePath}: ${error}. Defaulting to allow all.` ); this.patterns.push({ pattern: '**/*', isNegation: false, originalLine: '*', }); } } /** * Normalize a pattern for matching * Converts simple wildcards to proper glob patterns */ private normalizePattern(pattern: string): string { // If it's just a wildcard, convert to match all if (pattern === '*') { return '**/*'; } // If pattern is absolute, use it as-is if (pattern.startsWith('/')) { return pattern; } // For relative patterns, make them match anywhere in the tree if (!pattern.startsWith('**/')) { return `**/${pattern}`; } return pattern; } /** * Check if a path matches a glob pattern * @param path - Absolute path to check * @param pattern - Glob pattern to match against */ private matchesPattern(path: string, pattern: string): boolean { // Handle absolute patterns if (pattern.startsWith('/')) { return minimatch(path, pattern, { dot: true }); } // For relative patterns (starting with **/ or similar), check if path matches return minimatch(path, pattern, { dot: true, matchBase: true }); } }

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/Pr0j3c7t0dd-Ltd/cut-copy-paste-mcp'

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