Skip to main content
Glama

Cut-Copy-Paste Clipboard Server

ARCHITECTURE.md41.2 kB
# Architecture Documentation **MCP Cut-Copy-Paste Clipboard Server v1.0.0** This document describes the architectural design, component structure, data flows, and key design decisions for the MCP Cut-Copy-Paste Clipboard Server. --- ## Table of Contents 1. [System Overview](#system-overview) 2. [Architectural Principles](#architectural-principles) 3. [Component Architecture](#component-architecture) 4. [Data Flow](#data-flow) 5. [Database Design](#database-design) 6. [Design Decisions & Rationale](#design-decisions--rationale) 7. [Technology Stack](#technology-stack) 8. [Key Design Patterns](#key-design-patterns) 9. [Security Architecture](#security-architecture) 10. [Performance Considerations](#performance-considerations) 11. [Limitations & Trade-offs](#limitations--trade-offs) 12. [Future Architecture](#future-architecture) --- ## System Overview ### Purpose The MCP Cut-Copy-Paste Clipboard Server provides clipboard-style operations (cut, copy, paste, undo) for AI-assisted coding agents through the Model Context Protocol (MCP). It enables AI agents to manipulate code across files with line-level precision, supporting common refactoring workflows. ### High-Level Architecture ``` ┌─────────────────────────────────────────────────────────────────┐ │ MCP Client (Claude/Cursor) │ │ stdio transport │ └────────────────────────────────┬────────────────────────────────┘ │ MCP Protocol (JSON-RPC) ┌────────────────────────────────▼────────────────────────────────┐ │ ClipboardServer │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ Request Handlers (MCP SDK) │ │ │ │ ListTools │ CallTool (copy/cut/paste/undo/show/history)│ │ │ └──────────────────────┬───────────────────────────────────┘ │ │ │ │ │ ┌──────────────────────▼───────────────────────────────────┐ │ │ │ ClipboardTools │ │ │ │ Orchestration layer - implements 6 MCP tools │ │ │ └──────────────────────┬───────────────────────────────────┘ │ │ │ │ │ ┌──────────────────────┴───────────────────────────────────┐ │ │ │ Core Service Components │ │ │ │ │ │ │ │ ┌─────────────┐ ┌──────────────┐ ┌────────────────┐ │ │ │ │ │ FileHandler │ │ ClipboardMgr │ │ SessionManager │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ Read/Write │ │ Get/Set │ │ Create/Get │ │ │ │ │ │ Insert/Del │ │ Clear │ │ Cleanup │ │ │ │ │ └─────────────┘ └──────────────┘ └────────────────┘ │ │ │ │ │ │ │ │ ┌─────────────────────────┐ ┌────────────────────────┐ │ │ │ │ │ OperationLogger │ │ DatabaseManager │ │ │ │ │ │ │ │ │ │ │ │ │ │ Log Copy/Cut/Paste │ │ SQLite connection │ │ │ │ │ │ Track Undo History │ │ Transaction support │ │ │ │ │ └─────────────────────────┘ └────────────────────────┘ │ │ │ └────────────────────────────────────────────────────────────┘ │ └─────────────────────────────────┬───────────────────────────────┘ │ ┌────────────────────────┴───────────────────────┐ │ │ ▼ ▼ ┌─────────────────┐ ┌──────────────────┐ │ SQLite Database│ │ File System │ │ │ │ │ │ • sessions │ │ User workspace │ │ • clipboard │ │ files │ │ • operations │ │ │ │ • paste_history│ │ │ └─────────────────┘ └──────────────────┘ ``` ### Component Responsibilities | Layer | Components | Responsibility | |-------|------------|----------------| | **Protocol** | `ClipboardServer` | MCP protocol handling, request routing | | **Tools** | `ClipboardTools` | Tool orchestration, business logic coordination | | **Services** | `FileHandler`, `ClipboardManager`, `SessionManager`, `OperationLogger` | Core business logic, stateless operations | | **Persistence** | `DatabaseManager` | Database connection, transactions, schema | --- ## Architectural Principles ### 1. Layered Architecture The system follows a strict layered architecture with clear separation of concerns: - **Protocol Layer**: MCP server and request handlers - **Tool Layer**: MCP tool implementations and orchestration - **Service Layer**: Core business logic (file operations, clipboard, sessions) - **Persistence Layer**: Database and file system access **Principle**: Dependencies flow downward; lower layers have no knowledge of upper layers. ### 2. Single Responsibility Principle Each component has one clear responsibility: - `FileHandler`: File I/O operations with line-level precision - `ClipboardManager`: Clipboard state management - `SessionManager`: Session lifecycle management - `OperationLogger`: Audit trail and undo support - `DatabaseManager`: Database connection and transactions ### 3. Dependency Injection Components receive dependencies through constructor injection, enabling: - Easy testing with mocks - Clear dependency graph - Runtime flexibility ### 4. Fail-Fast Validation All inputs are validated at the earliest possible point: - Session IDs validated before any operation - File paths validated before file access - Line ranges validated before reading - Binary files rejected immediately ### 5. Atomic Operations Critical operations use database transactions to ensure atomicity: - Paste operations (multiple files + history) - Undo operations (file restoration + logging) - Cut operations (read + delete + clipboard update) ### 6. Explicit Over Implicit Design favors explicitness: - 1-indexed line numbers (matching editor conventions) - Explicit operation types (`'copy'` vs `'cut'`) - Clear error messages with context - TypeScript strict mode (no implicit `any`) --- ## Component Architecture ### Core Services Layer #### DatabaseManager **Purpose**: Manages SQLite database connection, schema initialization, and transaction support. **Key Responsibilities**: - Initialize database schema (4 tables + indexes) - Provide connection to all services - Support transactions with rollback - Run schema migrations **Design Decisions**: - WAL mode for better concurrency - Foreign key constraints for referential integrity - Indexes on commonly queried columns **File**: `src/lib/database.ts` #### SessionManager **Purpose**: Manages session lifecycle and automatic cleanup. **Key Responsibilities**: - Create sessions with cryptographically secure IDs - Validate and retrieve sessions - Update last activity timestamps ("touch" behavior) - Clean up expired sessions (24-hour timeout) **Design Decisions**: - Cryptographic randomness for session IDs (32 bytes → base64url) - Touch-on-read to maintain activity - Automatic cleanup on server start - No manual session termination (timeout-based only) **File**: `src/lib/session-manager.ts` #### FileHandler **Purpose**: Handles all file system operations with line-level manipulation. **Key Responsibilities**: - Read specific line ranges from files - Insert lines at arbitrary positions - Delete line ranges - Detect line endings (CRLF vs LF) - Binary file detection - File snapshots for undo **Design Decisions**: - Line numbers are 1-indexed (matches editors) - Line 0 supported for paste at file start - Preserves original line endings - 10MB file size limit - Binary detection using signatures + null bytes + UTF-8 validation **File**: `src/lib/file-handler.ts` **Binary Detection Strategy**: 1. Check file signatures (PNG, PDF, JPEG, GIF, ZIP) 2. Scan first 8KB for null bytes 3. Validate UTF-8 encoding 4. Count control characters (< 5% threshold) #### ClipboardManager **Purpose**: Manages per-session clipboard state in the database. **Key Responsibilities**: - Store clipboard content with metadata - Retrieve clipboard for paste operations - Track operation type (copy vs cut) - Store original file content for cut operations - Enforce 10MB clipboard size limit **Design Decisions**: - Single clipboard per session (no multiple clipboards) - Overwrites previous clipboard content - Stores `cutSourceOriginalContent` for undo support - Size validation before storage **File**: `src/lib/clipboard-manager.ts` #### OperationLogger **Purpose**: Maintains audit trail and enables undo functionality. **Key Responsibilities**: - Log all operations (copy, cut, paste, undo) - Store paste history with file snapshots - Retrieve last paste for undo - Provide operation history **Design Decisions**: - Separate tables for operations log and paste history - Paste history stores full file snapshots (before and after) - Cut operations store original file content - `undone` flag prevents double-undo - Transaction support for atomic paste+history **File**: `src/lib/operation-logger.ts` ### Tool Orchestration Layer #### ClipboardTools **Purpose**: Orchestrates core services to implement MCP tools. **Key Responsibilities**: - Implement 6 MCP tools - Coordinate between FileHandler, ClipboardManager, OperationLogger - Validate sessions before operations - Error handling and message formatting **Design Pattern**: Facade pattern - provides simplified interface to complex subsystem. **Tools Implemented**: 1. `copyLines`: Read → Store in clipboard → Log 2. `cutLines`: Snapshot → Read → Store → Log → Delete 3. `pasteLines`: Read clipboard → Snapshot targets → Insert → Log history 4. `showClipboard`: Retrieve clipboard metadata 5. `undoLastPaste`: Retrieve history → Restore files → Log undo 6. `getOperationHistory`: Query operation log **File**: `src/tools/clipboard-tools.ts` ### Protocol Layer #### ClipboardServer **Purpose**: MCP server implementation and request routing. **Key Responsibilities**: - Initialize MCP server with SDK - Register request handlers - Route tool calls to ClipboardTools - Format responses - Handle errors **Design Decisions**: - stdio transport (local only) - One session per server instance - Tool definitions loaded from config - JSON response format **File**: `src/server.ts` --- ## Data Flow ### Copy Operation Flow ``` 1. User/Agent → copy_lines(file, start, end) 2. ClipboardServer → Route to ClipboardTools.copyLines() 3. ClipboardTools → Validate session with SessionManager 4. ClipboardTools → FileHandler.readLines(file, start, end) 5. FileHandler → Read file, validate range, extract lines 6. FileHandler → Return {content, lines, lineEnding} 7. ClipboardTools → ClipboardManager.setClipboard(session, data) 8. ClipboardManager → Store in database (INSERT OR REPLACE) 9. ClipboardTools → OperationLogger.logCopy(session, details) 10. OperationLogger → Insert into operations_log 11. ClipboardTools → Return success response 12. ClipboardServer → Format JSON response 13. MCP Client ← Receive result ``` ### Cut Operation Flow ``` 1. User/Agent → cut_lines(file, start, end) 2. ClipboardServer → Route to ClipboardTools.cutLines() 3. ClipboardTools → Validate session 4. ClipboardTools → FileHandler.getFileSnapshot(file) ← Capture ORIGINAL state 5. ClipboardTools → FileHandler.readLines(file, start, end) 6. ClipboardTools → ClipboardManager.setClipboard(session, { content, operationType: 'cut', cutSourceOriginalContent: originalSnapshot ← Store for undo }) 7. ClipboardTools → OperationLogger.logCut(session, details) 8. ClipboardTools → FileHandler.deleteLines(file, start, end) 9. FileHandler → Read file, remove lines, write back 10. ClipboardTools → Return success response ``` ### Paste Operation Flow ``` 1. User/Agent → paste_lines([{file1, line1}, {file2, line2}]) 2. ClipboardServer → Route to ClipboardTools.pasteLines() 3. ClipboardTools → Validate session 4. ClipboardTools → ClipboardManager.getClipboard(session) 5. ClipboardTools → For each target: - FileHandler.getFileSnapshot(target) ← Capture BEFORE state 6. ClipboardTools → For each target: - FileHandler.insertLines(file, line, content.split('\n')) 7. ClipboardTools → OperationLogger.logPaste(session, { targets: [{filePath, targetLine, originalContent}], content, cutSourceFile: clipboard.sourceFile (if cut operation), cutSourceContent: clipboard.cutSourceOriginalContent (if cut) }) 8. OperationLogger → BEGIN TRANSACTION 9. OperationLogger → INSERT into operations_log 10. OperationLogger → INSERT into paste_history (per target) 11. OperationLogger → COMMIT TRANSACTION 12. ClipboardTools → Return success response ``` ### Undo Operation Flow ``` 1. User/Agent → undo_last_paste() 2. ClipboardServer → Route to ClipboardTools.undoLastPaste() 3. ClipboardTools → Validate session 4. ClipboardTools → OperationLogger.getLastPaste(session) 5. OperationLogger → Query paste_history WHERE undone = 0 6. ClipboardTools → For each target in paste history: - fs.writeFileSync(filePath, originalContent) ← Restore BEFORE state 7. ClipboardTools → If cutSourceFile exists: - fs.writeFileSync(cutSourceFile, cutSourceContent) ← Restore cut source 8. ClipboardTools → OperationLogger.logUndo(session, pasteOpId) 9. OperationLogger → BEGIN TRANSACTION 10. OperationLogger → INSERT into operations_log (type: 'undo') 11. OperationLogger → UPDATE paste_history SET undone = 1 12. OperationLogger → COMMIT TRANSACTION 13. ClipboardTools → Return {restoredFiles: [...]} ``` ### Critical Insight: Undo with Cut Operations When undoing a paste that originated from a cut operation: 1. **Paste targets** are restored to their state BEFORE the paste 2. **Cut source** is restored to its state BEFORE the cut (with the lines that were removed) This is achieved by storing `cutSourceOriginalContent` in the clipboard during cut operations, which is then propagated to `paste_history` during paste operations. --- ## Database Design ### Schema Overview The database uses SQLite with 4 main tables designed to support sessions, clipboard state, audit logging, and undo functionality. ### Tables #### 1. sessions **Purpose**: Track active sessions and enable timeout-based cleanup. ```sql CREATE TABLE sessions ( session_id TEXT PRIMARY KEY, created_at INTEGER NOT NULL, last_activity INTEGER NOT NULL ); CREATE INDEX idx_sessions_last_activity ON sessions(last_activity); ``` **Design Rationale**: - `session_id`: Cryptographically secure random ID (base64url encoded) - `last_activity`: Updated on every operation ("touch" behavior) - Index on `last_activity` for efficient cleanup queries #### 2. clipboard_buffer **Purpose**: Store per-session clipboard content and metadata. ```sql CREATE TABLE clipboard_buffer ( session_id TEXT PRIMARY KEY, content TEXT NOT NULL, source_file TEXT NOT NULL, start_line INTEGER NOT NULL, end_line INTEGER NOT NULL, copied_at INTEGER NOT NULL, operation_type TEXT NOT NULL CHECK(operation_type IN ('copy', 'cut')), cut_source_original_content TEXT, -- NULL for copy, full file content for cut FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE ); ``` **Design Rationale**: - `PRIMARY KEY` on `session_id`: One clipboard per session - `operation_type`: Distinguishes copy vs cut for undo behavior - `cut_source_original_content`: Stores ENTIRE original file before cut (for undo) - `INSERT OR REPLACE` semantics: New clipboard overwrites old #### 3. operations_log **Purpose**: Audit trail of all operations. ```sql CREATE TABLE operations_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, operation_type TEXT NOT NULL CHECK(operation_type IN ('copy', 'cut', 'paste', 'undo')), timestamp INTEGER NOT NULL, source_file TEXT, source_start_line INTEGER, source_end_line INTEGER, target_file TEXT, target_line INTEGER, content_snapshot TEXT, undoable INTEGER DEFAULT 1 CHECK(undoable IN (0, 1)), FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE ); CREATE INDEX idx_operations_log_session ON operations_log(session_id, timestamp DESC); ``` **Design Rationale**: - Auto-incrementing `id` for unique operation identification - `content_snapshot`: Stores content for history/debugging - `undoable`: Currently paste operations are undoable - Index on `(session_id, timestamp DESC)` for efficient history queries #### 4. paste_history **Purpose**: Enable undo functionality by storing file snapshots. ```sql CREATE TABLE paste_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, operation_log_id INTEGER NOT NULL, file_path TEXT NOT NULL, original_content TEXT NOT NULL, -- BEFORE paste modified_content TEXT NOT NULL, -- AFTER paste (not currently used) paste_line INTEGER NOT NULL, timestamp INTEGER NOT NULL, undone INTEGER DEFAULT 0 CHECK(undone IN (0, 1)), cut_source_file TEXT, -- If from cut: original source file cut_source_content TEXT, -- If from cut: original file content FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE, FOREIGN KEY (operation_log_id) REFERENCES operations_log(id) ON DELETE CASCADE ); CREATE INDEX idx_paste_history_session ON paste_history(session_id, timestamp DESC); CREATE INDEX idx_paste_history_undone ON paste_history(session_id, undone, timestamp DESC); ``` **Design Rationale**: - **Multiple rows per paste operation**: One row per target file - `original_content`: Full file content BEFORE paste (enables atomic restore) - `undone` flag: Prevents double-undo - `cut_source_file` + `cut_source_content`: Enables restoration of cut source during undo - Indexes optimize finding last undoable paste ### Data Flow for Undo **Key Design**: Full file snapshots (not diffs) for reliability. **Alternative Considered**: Store only diffs (inserted lines, positions) **Rejected Because**: - Difficult to reverse if file was modified between paste and undo - Complex edge cases (what if someone manually edited the pasted region?) - Full snapshot is simple, reliable, and trade-off is acceptable for v1 ### Foreign Key Cascades All child tables cascade on session deletion: - Deleting a session automatically removes clipboard, operations, and paste history - Ensures no orphaned data - Simplifies session cleanup --- ## Design Decisions & Rationale ### 1. SQLite Over JSON Files **Decision**: Use SQLite for persistent storage instead of JSON files. **Rationale**: - **ACID guarantees**: Transactions prevent partial updates - **Querying**: Easier to query history and filter by session/timestamp - **Indexes**: Performance for lookups (vs scanning JSON) - **Concurrency**: WAL mode handles concurrent access - **Referential integrity**: Foreign keys ensure consistency **Trade-offs**: - Slightly more complex than JSON - Requires migration strategy (implemented in `runMigrations()`) **Confidence**: ✅ Excellent choice for v1 ### 2. stdio Transport Only **Decision**: Use stdio transport, not HTTP. **Rationale**: - **Simpler implementation**: No auth, CORS, or TLS needed - **MCP convention**: stdio is standard for local MCP servers - **Security**: Local-only by default - **Scope**: HTTP planned for v2 **Trade-offs**: - Cannot be accessed remotely - One session per server instance (new process per client) **Confidence**: ✅ Appropriate for v1 local-only use case ### 3. 1-Indexed Line Numbers **Decision**: Use 1-indexed line numbers (not 0-indexed). **Rationale**: - **Editor convention**: Most editors show line 1 as first line - **User expectation**: Matches what users see in editors - **Clarity**: "Line 1" is more intuitive than "Line 0" **Trade-offs**: - Internal conversion needed (subtract 1 for array indexing) - Edge case: Line 0 supported for paste at file start (special case) **Confidence**: ✅ Correct decision for usability ### 4. Single Undo Level **Decision**: Support only single-level undo (last paste only). **Rationale**: - **Scope management**: Multi-level undo adds complexity - **v1 simplicity**: Sufficient for most use cases - **Database design**: Easily extendable to multi-level in v2 **Trade-offs**: - Cannot undo multiple operations - Cannot redo after undo **Future Enhancement**: v2 can add undo stack without schema changes (use `undone` as version number). **Confidence**: ✅ Pragmatic scope decision for v1 ### 5. Return Content on Copy/Cut **Decision**: Return clipboard content in copy/cut responses. **Rationale**: - **LLM benefit**: Agent can see what was copied without separate query - **Verification**: Agent can confirm correct lines were captured - **Debugging**: Easier to diagnose issues **Trade-offs**: - Larger response payloads (mitigated by 10MB limit) **Confidence**: ✅ Improves agent experience ### 6. Full File Snapshots for Undo **Decision**: Store full file content before paste (not diffs). **Rationale**: - **Simplicity**: No complex diff/patch logic needed - **Reliability**: Guaranteed restoration regardless of intervening changes - **Atomic**: Single write to restore file **Trade-offs**: - Higher database storage usage - Mitigated by: session cleanup (24h), 10MB clipboard limit **Alternative Considered**: Store only diffs **Rejected Because**: Complexity and edge cases outweigh storage savings for v1 **Confidence**: ✅ Correct trade-off for v1 reliability ### 7. Session-Based Clipboard (Not Global) **Decision**: Each session has independent clipboard. **Rationale**: - **Isolation**: Multiple agents can work independently - **No conflicts**: No race conditions on shared clipboard - **Session cleanup**: Clipboard automatically cleaned up with session **Trade-offs**: - Cannot share clipboard between sessions (by design) **Confidence**: ✅ Correct for multi-agent safety ### 8. Binary File Rejection **Decision**: Reject binary files explicitly. **Rationale**: - **Use case focus**: Designed for text code files - **Prevent errors**: Binary content would corrupt clipboard - **Clear errors**: Better than silent failures **Detection Strategy**: - File signatures (PNG, PDF, JPEG, GIF, ZIP) - Null byte detection - UTF-8 validation - Control character ratio **Trade-offs**: - Some text files might be incorrectly detected as binary (rare) - No support for binary code manipulation **Confidence**: ✅ Correct for code editing use case ### 9. 10MB File/Clipboard Limit **Decision**: Hard limit of 10MB for files and clipboard. **Rationale**: - **Memory safety**: Prevents memory exhaustion - **Reasonable limit**: Most code files are < 10MB - **Clear errors**: Fail fast with clear message **Trade-offs**: - Very large files cannot be processed - Future: Could add streaming for large files (v2) **Confidence**: ✅ Pragmatic safety limit ### 10. Automatic Session Cleanup **Decision**: 24-hour timeout, automatic cleanup on start. **Rationale**: - **No manual cleanup**: Zero maintenance for users - **Resource management**: Prevents database bloat - **Reasonable timeout**: 24 hours covers typical coding sessions **Trade-offs**: - Long-running sessions might expire (could be configurable in v2) **Confidence**: ✅ Good default behavior --- ## Technology Stack ### Core Dependencies | Technology | Version | Purpose | Rationale | |------------|---------|---------|-----------| | **TypeScript** | 5.7 | Type-safe development | Catch errors at compile time, better IDE support | | **Node.js** | ≥18.0.0 | Runtime | LTS version, modern ES modules support | | **better-sqlite3** | 11.7.0 | SQLite database | Synchronous API, native performance, reliability | | **@modelcontextprotocol/sdk** | 1.0.4 | MCP protocol | Official SDK, stdio transport support | ### Development Dependencies | Tool | Purpose | Rationale | |------|---------|-----------| | **Jest** | Testing framework | Industry standard, excellent TypeScript support | | **ts-jest** | TypeScript support for Jest | Direct TS test execution | | **ESLint** | Linting | Catch code quality issues | | **Prettier** | Code formatting | Consistent style, zero configuration debates | ### Why These Choices? #### TypeScript Strict Mode - Catch bugs early - Explicit return types on exported functions - No implicit `any` - Better refactoring support #### Better-SQLite3 - **Synchronous API**: Simpler than async for local operations - **Performance**: Native C++ bindings - **Reliability**: Mature, well-tested - **Features**: WAL mode, transactions, prepared statements **Alternative Considered**: `node-sqlite3` (async) **Rejected Because**: Synchronous is simpler and sufficient for local file operations #### ES Modules - Modern JavaScript standard - Tree-shaking for smaller bundles - Better for future npm package **Trade-off**: Import paths must include `.js` extension (TypeScript quirk) --- ## Key Design Patterns ### 1. Facade Pattern **Component**: `ClipboardTools` **Purpose**: Provides simplified interface to complex subsystem of file operations, clipboard management, and logging. **Benefits**: - Hides complexity from MCP server layer - Single entry point for each tool - Encapsulates coordination logic ### 2. Dependency Injection **Used Throughout**: All components receive dependencies via constructor. **Benefits**: - Easy testing with mocks - Clear dependency graph - Runtime flexibility **Example**: ```typescript constructor( private fileHandler: FileHandler, private clipboardManager: ClipboardManager, private operationLogger: OperationLogger, private sessionManager: SessionManager ) {} ``` ### 3. Repository Pattern **Components**: `ClipboardManager`, `SessionManager`, `OperationLogger` **Purpose**: Encapsulate data access logic, hide database details. **Benefits**: - Data access logic in one place - Easy to swap persistence layer - Clear API for data operations ### 4. Transaction Script Pattern **Component**: `OperationLogger.logPaste()` **Purpose**: Handle complex multi-step operations in a single transaction. **Implementation**: ```typescript return this.dbManager.transaction(() => { // 1. Insert operation log const opId = insertOperation(); // 2. Insert paste history for each target for (const target of targets) { insertHistory(opId, target); } return opId; }); ``` **Benefits**: - Atomic operations (all-or-nothing) - Automatic rollback on errors - Consistency guarantees ### 5. Snapshot Pattern **Component**: `FileHandler.getFileSnapshot()` **Purpose**: Capture complete file state for undo functionality. **Benefits**: - Simple restoration (no complex diffs) - Reliable (captures exact state) - Atomic undo (single write) --- ## Security Architecture ### Threat Model **In Scope**: - Local file system access (prevent unauthorized file access) - Path traversal attacks - Resource exhaustion (memory, disk) - Input validation - Session isolation - Clipboard data confidentiality at rest (AES-256-GCM encryption) **Out of Scope (v1)**: - Network attacks (no network access) - Authentication (local-only) ### Security Measures #### 1. Path Validation **Threat**: Path traversal (e.g., `../../etc/passwd`) **Mitigation**: ```typescript const resolvedPath = resolve(filePath); // Normalize path this.validateFilePath(resolvedPath); // Check existence this.validateWriteAccess(resolvedPath); // Check permissions ``` **Implementation**: `FileHandler` (lines 35, 102, 149, 197) #### 2. Input Validation **Threat**: Invalid inputs causing crashes or unexpected behavior **Mitigation**: - Type checking (TypeScript) - Range validation (line numbers > 0, start ≤ end) - Boundary checks (line range within file length) - Session ID validation (non-empty string) **Implementation**: All components validate inputs at entry points #### 3. Session Isolation **Threat**: Cross-session data access **Mitigation**: - Session validation before every operation - Database-level isolation (WHERE session_id = ?) - No shared state between sessions **Implementation**: `ClipboardTools` validates session at start of every tool method #### 4. Resource Limits **Threat**: Memory exhaustion, disk space exhaustion **Mitigation**: - 10MB file size limit - 10MB clipboard size limit - Binary file rejection - Session timeout (24 hours) **Implementation**: - `FileHandler.MAX_FILE_SIZE` - `ClipboardManager.MAX_CONTENT_SIZE` - Automatic session cleanup #### 5. Clipboard Encryption **Threat**: Local attackers reading the SQLite database to obtain clipboard contents. **Mitigation**: - AES-256-GCM encryption for clipboard payloads (content + metadata). - 32-byte per-installation key stored beside the database (e.g., `~/.mcp-clipboard/clipboard.key`) with 0o600 permissions. - Automatic key generation on first run; in-memory keys for `:memory:` databases. - Integrity protection via GCM authentication tags. - Legacy plaintext rows automatically migrated on next write. - Directory and database files hardened to 0o700 / 0o600 permissions at startup. **Implementation**: - `DatabaseManager` loads/creates encryption keys and exposes them to higher layers. - `ClipboardManager` encrypts on `setClipboard` and decrypts on `getClipboard`, falling back to plaintext for pre-encryption rows. #### 6. Error Message Safety **Threat**: Information leakage via error messages **Mitigation**: - Error messages contain only metadata (paths, line numbers) - No file content in errors - Stack traces not exposed to client - Errors wrapped at tool layer **Implementation**: `ClipboardTools` catches and wraps all errors #### 7. Cryptographically Secure Session IDs **Threat**: Session ID prediction **Mitigation**: ```typescript randomBytes(32).toString('base64') .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); ``` **Implementation**: `SessionManager.generateSecureId()` **Properties**: - 32 bytes of entropy (256 bits) - URL-safe base64 encoding - No predictable patterns #### 8. Database Security **Threat**: SQL injection **Mitigation**: - Prepared statements (parameterized queries) - No string concatenation for SQL - TypeScript prevents many injection vectors **Example**: ```typescript db.prepare('SELECT * FROM sessions WHERE session_id = ?').get(sessionId); ``` ### Security Limitations (Acknowledged) 1. **No path whitelist**: Can access any file user has permissions for 2. **No rate limiting**: Rapid operations not throttled 3. **No audit log for security events**: Only operation log 4. **No file integrity checks**: No hashing/verification **Mitigation**: These are deferred to v2 (see Future Architecture). --- ## Performance Considerations ### Design for Performance #### 1. Database Indexes **Indexes Created**: ```sql CREATE INDEX idx_sessions_last_activity ON sessions(last_activity); CREATE INDEX idx_operations_log_session ON operations_log(session_id, timestamp DESC); CREATE INDEX idx_paste_history_session ON paste_history(session_id, timestamp DESC); CREATE INDEX idx_paste_history_undone ON paste_history(session_id, undone, timestamp DESC); ``` **Purpose**: Optimize common queries (session cleanup, history retrieval, undo lookup) **Impact**: Sub-millisecond query times for typical operations #### 2. WAL Mode **Configuration**: `db.pragma('journal_mode = WAL')` **Benefits**: - Readers don't block writers - Better concurrency - Faster commits **Trade-off**: Slightly more disk space (WAL file) #### 3. Binary File Early Rejection **Strategy**: Check first 8KB only (not entire file) **Implementation**: `BINARY_CHECK_BYTES = 8000` **Benefits**: - Fast rejection of large binary files - Sufficient for reliable detection #### 4. Prepared Statements **All database queries use prepared statements** (cached by better-sqlite3) **Benefits**: - Query plan cached - Faster subsequent executions - SQL injection prevention #### 5. File Size Limits **10MB limit enforced before reading file** **Benefits**: - Prevents memory exhaustion - Fast failure for oversized files - Predictable memory usage ### Performance Benchmarks (From Testing) | Operation | File Size | Time | Status | |-----------|-----------|------|--------| | Copy 100 lines | < 100 lines | < 50ms | ✅ | | Copy 100 lines | 10,000 lines | < 200ms | ✅ | | Paste 100 lines | 1,000 lines | < 500ms | ✅ | | Multi-paste (3 targets) | Various | < 2s | ✅ | | Undo paste | 3 targets | < 1s | ✅ | ### Performance Limitations 1. **Full file reads**: Even for small line ranges (mitigated by 10MB limit) 2. **Full file writes**: Undo requires full file write (simplicity trade-off) 3. **No streaming**: Large files processed in memory (v2 enhancement) ### Future Optimizations (v2+) - Streaming for very large files - Incremental file updates (seek + write region) - Clipboard compression for large content - Connection pooling (if HTTP transport added) --- ## Limitations & Trade-offs ### By Design (Intentional) | Limitation | Rationale | Future Plan | |------------|-----------|-------------| | **Text files only** | Code editing focus, binary would corrupt clipboard | Unlikely to change | | **Single undo level** | V1 scope management, sufficient for most cases | V2: Undo stack | | **stdio transport only** | Simpler, secure, MCP convention | V2: HTTP option | | **10MB file limit** | Safety, reasonable for code files | V2: Configurable | | **10MB clipboard limit** | Memory safety | V2: Configurable | | **24-hour session timeout** | Resource management | V2: Configurable | | **No clipboard history** | Single clipboard per session | V2: Maybe | ### Technical Trade-offs #### Full File Snapshots vs Diffs **Chosen**: Full file snapshots for undo **Trade-off**: Higher storage usage **Justification**: Simplicity and reliability outweigh storage cost for v1 #### Synchronous vs Async API **Chosen**: Synchronous (better-sqlite3) **Trade-off**: Blocks event loop during DB operations **Justification**: Local operations are fast, simplicity benefit is significant #### 1-Indexed Line Numbers **Chosen**: 1-indexed (editor convention) **Trade-off**: Internal conversion needed (subtract 1 for arrays) **Justification**: User-facing API should match editor expectations --- ## Future Architecture ### v2.0 Enhancements #### 1. HTTP Transport Support **Goal**: Enable remote access and web-based clients **Architecture Changes**: ``` ┌─────────────────┐ │ MCP Client │ │ (Web/Remote) │ └────────┬────────┘ │ HTTPS ┌────────▼────────┐ │ HTTP Server │ ← New component │ (Express/Hapi) │ │ - Auth │ │ - CORS │ │ - Rate limit │ └────────┬────────┘ │ ┌────────▼────────┐ │ ClipboardServer │ ← Minimal changes │ (transport- │ │ agnostic) │ └─────────────────┘ ``` **Considerations**: - Authentication (JWT, API keys) - TLS/HTTPS - CORS configuration - Rate limiting per user - Session management (tie to auth) #### 2. Multi-Level Undo Stack **Goal**: Undo multiple operations **Schema Changes** (backward compatible): ```sql ALTER TABLE paste_history ADD COLUMN undo_version INTEGER DEFAULT 0; ``` **Logic**: - `undo_version = 0`: Current state - `undo_version = 1`: First undo - `undo_version = 2`: Second undo - Redo: Restore `undo_version - 1` **API**: - `undo_last_paste(count: number)`: Undo N operations - `redo_last_undo()`: New tool #### 3. Large File Streaming **Goal**: Support files > 10MB **Architecture**: ```typescript class StreamingFileHandler { async readLinesStreaming( filePath: string, startLine: number, endLine: number ): AsyncIterator<string> { // Read line-by-line, yield as available } } ``` **Considerations**: - Async API required - Clipboard storage (stream to temp file?) - Paste (stream from temp file) #### 4. Path Whitelist Configuration **Goal**: Restrict file access to specific directories **Configuration**: ```typescript interface ClipboardServerConfig { allowedPaths: string[]; deniedPaths: string[]; pathValidationMode: 'whitelist' | 'blacklist' | 'none'; } ``` **Implementation**: ```typescript validatePath(filePath: string): void { const resolved = resolve(filePath); if (this.config.pathValidationMode === 'whitelist') { if (!this.config.allowedPaths.some(p => resolved.startsWith(p))) { throw new Error('Path not in whitelist'); } } } ``` #### 5. Configurable Limits **Goal**: Make limits configurable per deployment **Configuration**: ```typescript interface PerformanceConfig { maxFileSize: number; // Default: 10MB maxClipboardSize: number; // Default: 10MB sessionTimeout: number; // Default: 24 hours binaryCheckBytes: number; // Default: 8000 } ``` ### Backward Compatibility Strategy **Principle**: v2 enhancements should not break v1 clients **Approach**: - Additive API changes only (new tools, optional parameters) - Schema migrations (ALTER TABLE, not DROP/CREATE) - Feature flags for new behavior - Version negotiation in MCP handshake (if needed) --- ## Conclusion The MCP Cut-Copy-Paste Clipboard Server architecture prioritizes: 1. **Simplicity**: Clean layered architecture, clear responsibilities 2. **Reliability**: ACID transactions, full snapshots, comprehensive testing 3. **Security**: Input validation, session isolation, resource limits 4. **Usability**: 1-indexed lines, clear errors, explicit operations 5. **Extensibility**: Layered design enables v2 enhancements The design trade-offs favor simplicity and reliability for v1, with a clear path to v2 enhancements (HTTP transport, multi-level undo, streaming) that can be added without breaking existing functionality. **Key Architectural Strengths**: - Strict separation of concerns (protocol, tools, services, persistence) - Dependency injection for testability - Transaction-based operations for consistency - Full file snapshots for reliable undo - Comprehensive error handling and validation **Production Readiness**: ✅ Architecture is well-suited for v1.0.0 release with clear future evolution path.

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