ARCHITECTURE.md•41.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.