package tools import ( "context" "code.nochebuena.dev/einherjar/mcp/internal/index" "code.nochebuena.dev/einherjar/mcp/internal/rules" "github.com/modelcontextprotocol/go-sdk/mcp" ) type validateSnippetInput struct { Code string `json:"code" jsonschema:"Go source code to validate against Einherjar conventions. A full file is preferred; a partial body will be wrapped automatically."` } type validateSnippetOutput struct { Findings []rules.Finding `json:"findings"` Summary string `json:"summary"` } func registerValidateSnippet(s *mcp.Server, _ *index.Index) { mcp.AddTool(s, &mcp.Tool{ Name: "validate_snippet", Description: "Validate a Go snippet against Einherjar wiring conventions: lifecycle setup, logger configuration, env-var handling, server registration. Findings are advisory, not a substitute for go vet or the project's tests.", }, func(ctx context.Context, req *mcp.CallToolRequest, args validateSnippetInput) (*mcp.CallToolResult, validateSnippetOutput, error) { findings := rules.Run(args.Code) if findings == nil { findings = []rules.Finding{} } summary := summarise(findings) out := validateSnippetOutput{Findings: findings, Summary: summary} return jsonText(out), out, nil }) } func summarise(fs []rules.Finding) string { if len(fs) == 0 { return "No issues found — snippet follows Einherjar conventions." } var errs, warns, infos int for _, f := range fs { switch f.Severity { case rules.SeverityError: errs++ case rules.SeverityWarning: warns++ case rules.SeverityInfo: infos++ } } return pluralise(errs, "error", "errors") + ", " + pluralise(warns, "warning", "warnings") + ", " + pluralise(infos, "note", "notes") } func pluralise(n int, singular, plural string) string { if n == 1 { return "1 " + singular } return itoa(n) + " " + plural } func itoa(n int) string { if n == 0 { return "0" } neg := n < 0 if neg { n = -n } var buf [20]byte i := len(buf) for n > 0 { i-- buf[i] = byte('0' + n%10) n /= 10 } if neg { i-- buf[i] = '-' } return string(buf[i:]) }