Claude Skills Guide

Claude Code for Custom LSP Diagnostics Workflow

The Language Server Protocol (LSP) has revolutionized how we approach code analysis and diagnostics. By combining Claude Code with custom LSP diagnostics workflows, developers can create powerful automated systems that catch errors, enforce coding standards, and provide intelligent feedback—all without leaving their development environment.

This guide walks you through building a custom LSP diagnostics workflow using Claude Code, with practical examples you can adapt for your own projects.

Understanding LSP Diagnostics in Claude Code

LSP diagnostics are standardized messages that language servers send to clients to report errors, warnings, and other issues in your code. Claude Code can interact with these diagnostics to help you build workflows that automatically detect, categorize, and respond to code issues.

When you work with LSP-enabled editors, diagnostics appear as squiggly lines under problematic code. But with Claude Code, you can go beyond visual indicators—you can capture, filter, and act on these diagnostics programmatically.

Setting Up Your Diagnostic Collection

The first step in building a custom diagnostics workflow is capturing LSP messages. Here’s a basic setup using Claude Code’s tools:

import subprocess
import json

def get_lsp_diagnostics(lsp_server, file_path):
    """Capture diagnostics from an LSP server for a given file."""
    # Initialize LSP server and send didOpen notification
    initialize_msg = {
        "jsonrpc": "2.0",
        "id": 1,
        "method": "initialize",
        "params": {
            "processId": None,
            "rootUri": "file:///your/project/root",
            "capabilities": {}
        }
    }
    
    # Send document open notification
    did_open_msg = {
        "jsonrpc": "2.0",
        "method": "textDocument/didOpen",
        "params": {
            "textDocument": {
                "uri": f"file://{file_path}",
                "languageId": "python",
                "version": 1,
                "text": open(file_path).read()
            }
        }
    }
    
    # Parse diagnostics from server response
    return parse_diagnostics_response(response)

This foundation lets you build more sophisticated workflows that process diagnostics in real-time.

Building a Custom Diagnostics Pipeline

A well-designed diagnostics pipeline has three main stages: collection, analysis, and action. Let’s build each component.

Stage 1: Collection

Create a Claude Code skill that intercepts diagnostics from your language server:

{
  "name": "diagnostics-collector",
  "description": "Collect and aggregate LSP diagnostics",
  "actions": [
    {
      "trigger": "on-save",
      "execute": "capture_current_diagnostics"
    }
  ]
}

Stage 2: Analysis

Once collected, diagnostics need categorization. Here’s a pattern for analyzing and grouping issues:

def analyze_diagnostics(diagnostics):
    """Categorize diagnostics by severity and type."""
    categorized = {
        "errors": [],
        "warnings": [],
        "information": [],
        "by_file": {},
        "by_type": {}
    }
    
    for diag in diagnostics:
        severity = diag.get("severity", 3)
        if severity == 1:
            categorized["errors"].append(diag)
        elif severity == 2:
            categorized["warnings"].append(diag)
        else:
            categorized["information"].append(diag)
        
        # Group by file
        file = diag.get("uri", "").split("/")[-1]
        categorized["by_file"].setdefault(file, []).append(diag)
        
        # Group by error code
        code = diag.get("code", "unknown")
        categorized["by_type"].setdefault(code, []).append(diag)
    
    return categorized

Stage 3: Action

The final stage is taking action based on your analysis. This could mean generating reports, triggering notifications, or automatically creating fix suggestions:

def generate_diagnostic_report(analyzed, output_path):
    """Create a formatted diagnostic report."""
    report = []
    report.append("# Diagnostics Report\n")
    
    report.append(f"## Summary")
    report.append(f"- Errors: {len(analyzed['errors'])}")
    report.append(f"- Warnings: {len(analyzed['warnings'])}")
    report.append(f"- Info: {len(analyzed['information'])}\n")
    
    if analyzed['errors']:
        report.append("## Critical Errors\n")
        for error in analyzed['errors']:
            report.append(f"- **{error['source']}**: {error['message']}")
            if 'range' in error:
                report.append(f"  Location: {error['range']}")
            report.append("")
    
    return "\n".join(report)

Practical Example: Git Pre-Commit Diagnostics

One powerful use case is running diagnostics before commits. Here’s how to integrate with git hooks:

#!/bin/bash
# .git/hooks/pre-commit

# Run diagnostics on staged files
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)

for file in $STAGED_FILES; do
    echo "Running diagnostics on: $file"
    claude-code run-diagnostics --file "$file" --format json
done

# Exit with error if critical issues found
if [ $? -ne 0 ]; then
    echo "Critical diagnostics found. Commit aborted."
    exit 1
fi

This workflow ensures code quality before it enters your repository, catching issues early in the development cycle.

Advanced: Custom Diagnostic Rules

Beyond standard LSP diagnostics, you can create custom rules specific to your project:

// Define custom diagnostic rules
const customRules = {
    "no-console-log": {
        "severity": "warning",
        "message": "Use a proper logging framework instead of console.log",
        "pattern": /console\.(log|debug|info)/
    },
    "auth-check-required": {
        "severity": "error",
        "message": "Route handler must include authentication check",
        "pattern": /router\.(get|post|put|delete)\(.*\)$/m,
        "requires": ["authMiddleware"]
    }
};

// Apply custom rules alongside LSP diagnostics
function applyCustomRules(sourceCode, lspDiagnostics) {
    const customIssues = [];
    
    for (const [ruleName, rule] of Object.entries(customRules)) {
        const matches = sourceCode.matchAll(rule.pattern);
        for (const match of matches) {
            customIssues.push({
                rule: ruleName,
                message: rule.message,
                severity: rule.severity,
                position: match.index
            });
        }
    }
    
    return [...lspDiagnostics, ...customIssues];
}

Best Practices for Diagnostic Workflows

When building your custom LSP diagnostics workflow, keep these principles in mind:

  1. Prioritize by severity: Not all diagnostics are equal. Focus on errors first, then warnings, and finally informational messages.

  2. Aggregate strategically: Instead of treating each diagnostic in isolation, look for patterns. A hundred similar warnings might indicate a systemic issue worth addressing holistically.

  3. Integrate with your tooling: Your diagnostics workflow should play well with existing tools—linters, formatters, and CI/CD pipelines.

  4. Provide actionable feedback: When diagnostics are detected, the action should be clear. Don’t just say “there’s an error”; explain what to do about it.

  5. Cache intelligently: Running full diagnostics on every keystroke is expensive. Implement debouncing and caching to balance responsiveness with accuracy.

Conclusion

Building custom LSP diagnostics workflows with Claude Code opens up powerful possibilities for automated code quality assurance. By collecting diagnostics, analyzing patterns, and taking targeted actions, you can catch issues early, enforce standards consistently, and focus your attention on what matters most—writing great code.

Start with simple workflows and iterate. The key is finding the right balance between thoroughness and performance, ensuring your diagnostics enhance rather than hinder your development process.

Built by theluckystrike — More at zovo.one