- [x] Add API reference documentation with tool descriptions and examples - [x] Add ERROR_CODES reference with error descriptions and remediation steps - [x] Add PERFORMANCE tuning guide with caching and optimization details - [x] Add GitHub Actions workflows for linting and security scanning - [x] Add golangci-lint configuration with comprehensive linter settings - [x] Add pre-commit hooks configuration for local development - [x] Add API documentation generator tool (cmd/docgen) - [x] Update Go version from 1.24 to 1.25 across workflows - [x] Add static build configuration to goreleaser - [x] Add metrics package with Prometheus-style metric types - [x] Add parser benchmarks for performance testing - [x] Add LSP manager integration tests - [x] Add server integration tests with MCP protocol flow testing - [x] Extract regex cache to shared utility package - [x] Add context cancellation handling in AST queries - [x] Add graceful shutdown with timeout to server - [x] Add configurable max parse size (MaxParseSize) - [x] Add Config.Validate() method with comprehensive checks - [x] Add parser cache statistics tracking - [x] Add file permission preservation in edit operations - [x] Improve line splitting for large files with bufio.Scanner - [x] Add comprehensive config tests for edge cases - [x] Update Makefile with new targets and documentation
13 KiB
MCP Filepuff Performance Tuning Guide
This guide provides detailed information on optimizing mcp-filepuff performance, understanding resource usage, and configuring the server for your workload.
Table of Contents
- Parser Cache Configuration
- File Size Limits
- LSP Configuration
- Memory Usage Patterns
- Benchmarking
- Production Recommendations
Parser Cache Configuration
The parser cache is critical for performance as it avoids re-parsing files that haven't changed.
How the Cache Works
┌─────────────────────────────────────────────────────────────┐
│ Parse Request │
│ (file, content) │
└─────────────────────────┬───────────────────────────────────┘
│
▼
┌──────────────────────┐
│ Content Hash Check │
│ (xxHash64) │
└──────────┬───────────┘
│
┌───────────┴───────────┐
│ │
▼ ▼
Cache Hit Cache Miss
│ │
▼ ▼
Return cached tree Parse with Tree-sitter
│ │
│ ▼
│ Store in LRU cache
│ │
└───────────┬───────────┘
│
▼
Return ParseResult
Cache Statistics
The parser tracks detailed statistics:
type CacheStatsResult struct {
Hits int64 // Number of cache hits
Misses int64 // Number of cache misses
HitRate float64 // Ratio of hits to total requests
Size int // Current number of cached items
TotalParseTime int64 // Total time spent parsing (nanoseconds)
ParseCount int64 // Number of parse operations
AvgParseTime int64 // Average parse time (nanoseconds)
LastParseTime int64 // Most recent parse duration
}
Cache Configuration
The LRU cache holds up to 100 parsed AST trees by default. This is sufficient for most development workflows where you interact with a subset of files.
Cache Key: xxHash64 of file content (extremely fast, ~5GB/s)
Eviction Policy: Least Recently Used (LRU) - when the cache is full, the least recently accessed entry is evicted.
Optimizing Cache Performance
-
Batch Related Operations: When working on related files, perform all operations on one file before moving to the next. This maximizes cache hits.
-
Monitor Hit Rate: A healthy cache has >80% hit rate. Lower rates suggest:
- Working with too many files simultaneously
- Files changing frequently between operations
-
Cache Invalidation: The cache is content-based (hash), so modified files automatically get re-parsed.
File Size Limits
Default Limits
| Limit | Default Value | Environment Variable |
|---|---|---|
| Max File Size | 10 MB | - |
| Max Parse Size | 10 MB | - |
| Max Edit Size | 100 KB | - |
| Max Search Results | 1000 | - |
Configuration
Configure via .mcp-filepuff.json in workspace root:
{
"max_file_size": 10485760,
"max_parse_size": 10485760,
"max_search_results": 1000,
"max_edit_size": 102400
}
Understanding Limits
Max File Size (10 MB)
- Maximum file size that can be read via
file_read - Prevents memory exhaustion with large files
- Increase for codebases with large generated files
Max Parse Size (10 MB)
- Maximum file size for AST parsing
- Tree-sitter parsing memory usage is ~3-5x file size
- A 10 MB file needs ~30-50 MB RAM for parsing
Max Edit Size (100 KB)
- Maximum size for files being edited
- Keeps diff generation fast
- Prevents accidental edits to large generated files
Token-Efficient Reading
For large files, use token-efficient options:
// Get only symbol summary (~90-98% token reduction)
{"path": "large_file.go", "include_ast": true, "symbols_only": true}
// Limit output lines
{"path": "large_file.go", "max_lines": 50}
// Read specific line range
{"path": "large_file.go", "line_start": 100, "line_end": 150}
LSP Configuration
Timeout Configuration
# LSP operation timeout (default: 5 minutes)
export MCP_LSP_TIMEOUT="5m"
# Search timeout (default: 30 seconds)
export MCP_SEARCH_TIMEOUT="30s"
LSP Server Lifecycle
┌─────────────────────────────────────────────────────────────┐
│ LSP Request │
│ (hover, definition, references) │
└─────────────────────────┬───────────────────────────────────┘
│
▼
┌──────────────────────┐
│ Check Server Pool │
│ (by language) │
└──────────┬───────────┘
│
┌───────────┴───────────┐
│ │
▼ ▼
Server Exists No Server
│ │
▼ ▼
Update lastUsed Start New Server
│ │
│ ▼
│ Initialize (handshake)
│ │
└───────────┬───────────┘
│
▼
┌─────────────────┐
│ Open Document │
│ (if not open) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Execute LSP │
│ Request │
└────────┬────────┘
│
▼
Return Result
Server Pool Management
- Idle Timeout: 5 minutes (servers closed after inactivity)
- Pool Reaper: Checks every 60 seconds for idle servers
- One Server Per Language: Efficient resource usage
Optimizing LSP Performance
-
First Request Latency: Initial LSP requests are slow due to server startup and project indexing. Subsequent requests are fast.
-
gopls Optimization: For Go projects, gopls performance depends on module cache:
# Pre-populate module cache go mod download -
typescript-language-server: Ensure
node_modulesis populated:npm install -
clangd: Requires
compile_commands.jsonfor best results:# Generate with CMake cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .
Memory Usage Patterns
Component Memory Usage
| Component | Memory Pattern | Notes |
|---|---|---|
| Parser Registry | Per-language parsers | ~5-10 MB per language |
| AST Cache | LRU, 100 entries max | ~50-200 MB typically |
| LSP Servers | External processes | ~100-500 MB per server |
| Search (ripgrep) | Streaming | Minimal memory |
| Edit Engine | Per-operation | Proportional to file size |
Memory Calculation Example
For a typical Go project:
Base Server: ~20 MB
Go Parser: ~10 MB
AST Cache (50 files): ~100 MB
gopls: ~300 MB
────────────────────────────────
Total: ~430 MB
Reducing Memory Usage
-
Disable LSP: If you don't need go-to-definition/references:
export MCP_ENABLE_LSP="false"This saves ~100-500 MB per language server.
-
Reduce Cache Size: For memory-constrained environments, you can recompile with a smaller cache size (requires code change).
-
Close Idle Servers: LSP servers are automatically closed after 5 minutes of inactivity.
Benchmarking
Running Benchmarks
The project includes comprehensive benchmarks:
# Run all benchmarks
go test -bench=. ./...
# Run parser benchmarks with memory stats
go test -bench=. -benchmem ./internal/parser/...
# Run with specific count for stability
go test -bench=. -count=5 ./internal/parser/...
Available Benchmarks
Parser Benchmarks (internal/parser/parser_bench_test.go):
BenchmarkParseGo- Go file parsingBenchmarkParseTypeScript- TypeScript file parsingBenchmarkParsePython- Python file parsingBenchmarkParseC- C file parsingBenchmarkParseCpp- C++ file parsingBenchmarkCacheHit- Cache hit performanceBenchmarkCacheMiss- Cache miss performanceBenchmarkContentHash- xxHash performanceBenchmarkExtractSymbols- Symbol extraction
Expected Performance
Typical benchmark results (M1 Mac):
BenchmarkParseGo-8 5000 220000 ns/op 45000 B/op 850 allocs/op
BenchmarkParseTypeScript-8 3000 380000 ns/op 62000 B/op 1200 allocs/op
BenchmarkCacheHit-8 500000 2400 ns/op 128 B/op 3 allocs/op
BenchmarkContentHash-8 2000000 600 ns/op 0 B/op 0 allocs/op
Key observations:
- Cache hits are ~100x faster than cache misses
- Content hashing is extremely fast (xxHash64)
- Parsing speed varies by language complexity
Profiling
# CPU profiling
go test -bench=BenchmarkParseGo -cpuprofile=cpu.prof ./internal/parser/...
go tool pprof cpu.prof
# Memory profiling
go test -bench=BenchmarkParseGo -memprofile=mem.prof ./internal/parser/...
go tool pprof mem.prof
# Generate flame graph (requires pprof)
go tool pprof -http=:8080 cpu.prof
Production Recommendations
Environment Variables
# Essential configuration
export MCP_WORKSPACE_ROOT="/path/to/workspace"
export MCP_LSP_TIMEOUT="5m"
export MCP_SEARCH_TIMEOUT="30s"
export MCP_ENABLE_LSP="true"
# Optional optimizations
export MCP_FOLLOW_SYMLINKS="true"
export MCP_RESPECT_GITIGNORE="true"
Logging Configuration
# Development
./mcp-filepuff -log-level debug -log-file /tmp/mcp-filepuff.log
# Production (minimal logging)
./mcp-filepuff -log-level warn
Health Monitoring
Use the ping tool to verify server health:
{"tool": "ping"}
Expected response: "pong"
Performance Checklist
- Language servers installed and in PATH
- Project initialized (go.mod, package.json, etc.)
- Reasonable file size limits for your codebase
- LSP timeout appropriate for project size
- Adequate system memory (recommend 2+ GB free)
Troubleshooting Slow Performance
-
Slow Initial Operations
- LSP servers need to index project
- Wait for initial indexing to complete
- Check LSP server logs for progress
-
Slow Search
- Check for overly broad patterns
- Exclude large directories (node_modules, vendor)
- Verify .gitignore is respected
-
High Memory Usage
- Disable unused LSP servers
- Check for memory leaks in language servers
- Monitor cache size
-
Timeouts
- Increase timeout values
- Check for I/O bottlenecks
- Verify network filesystems are responsive
See Also
- API.md - Complete API reference
- ERROR_CODES.md - Error code reference
- README.md - Getting started