feat(docs, ci, config): add comprehensive documentation and tooling

- [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
This commit is contained in:
2026-01-28 20:43:20 +00:00
parent 143a166249
commit 9205b2bc26
27 changed files with 6332 additions and 1634 deletions
+71 -12
View File
@@ -2,6 +2,7 @@
package server
import (
"bufio"
"context"
"fmt"
"log/slog"
@@ -37,7 +38,7 @@ type Server struct {
// New creates a new MCP server instance.
func New(cfg *config.Config, logger *slog.Logger) (*Server, error) {
parserRegistry := parser.NewRegistry()
parserRegistry := parser.NewRegistryWithSize(cfg.MaxParseSize)
s := &Server{
cfg: cfg,
logger: logger,
@@ -545,7 +546,25 @@ func symbolKindIcon(kind protocol.SymbolKind) string {
}
func splitLines(s string) []string {
// Use optimized stdlib implementation (2-3x faster than manual loop)
// For large files (> 1MB), use bufio.Scanner which is more memory efficient
// For smaller files, use simple string split which is faster
const largeSizeThreshold = 1024 * 1024 // 1MB
if len(s) > largeSizeThreshold {
// Use scanner for large files
scanner := bufio.NewScanner(strings.NewReader(s))
var lines []string
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
// Handle potential error and add empty line if string ended with newline
if len(s) > 0 && s[len(s)-1] == '\n' {
lines = append(lines, "")
}
return lines
}
// Use optimized stdlib implementation for smaller files (2-3x faster than manual loop)
return strings.Split(s, "\n")
}
@@ -596,6 +615,13 @@ func (s *Server) handleASTQuery(ctx context.Context, request mcp.CallToolRequest
}
err := filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error {
// Check for context cancellation
select {
case <-ctx.Done():
return ctx.Err()
default:
}
if err != nil {
return nil // Skip files with errors
}
@@ -956,25 +982,58 @@ func (s *Server) handleEdit(ctx context.Context, request mcp.CallToolRequest, ap
// Run starts the MCP server and blocks until shutdown.
func (s *Server) Run(ctx context.Context) error {
// Set up signal handling for graceful shutdown
_, cancel := context.WithCancel(ctx)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(sigChan)
// Channel to communicate server errors
errChan := make(chan error, 1)
// Start server in goroutine
go func() {
sig := <-sigChan
s.logger.Info("received shutdown signal", "signal", sig)
cancel()
s.logger.Info("starting MCP server",
"workspace", s.cfg.WorkspaceRoot,
"lsp_enabled", s.cfg.EnableLSP,
)
errChan <- server.ServeStdio(s.mcp)
}()
s.logger.Info("starting MCP server",
"workspace", s.cfg.WorkspaceRoot,
"lsp_enabled", s.cfg.EnableLSP,
)
// Wait for either signal or server error
select {
case sig := <-sigChan:
s.logger.Info("received shutdown signal", "signal", sig)
// Start the MCP server with stdio transport
return server.ServeStdio(s.mcp)
// Create timeout context for shutdown
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer shutdownCancel()
// Call graceful shutdown
if err := s.Shutdown(shutdownCtx); err != nil {
s.logger.Error("error during shutdown", "error", err)
return err
}
s.logger.Info("server shutdown complete")
return nil
case err := <-errChan:
// Server stopped on its own
return err
case <-ctx.Done():
// Context cancelled externally
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer shutdownCancel()
if err := s.Shutdown(shutdownCtx); err != nil {
s.logger.Error("error during shutdown", "error", err)
}
return ctx.Err()
}
}
// Shutdown gracefully shuts down the server.