Files
zzet--gortex/internal/mcp/tools_analyze_connectivity.go
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

77 lines
2.7 KiB
Go

// connectivity_health — a graph-EXTRACTION quality diagnostic.
//
// This analyzer reports the connectivity health of the knowledge graph
// itself: how many nodes are isolated (zero edges of any kind), how
// many are leaves / source-only / sink-only, the effective-vs-nominal
// graph size, and which source files contribute the most isolated /
// leaf nodes.
//
// It is deliberately DISTINCT from kind=dead_code. dead_code reports
// symbols with zero *incoming usage* edges — genuinely unreachable
// code, a real finding to act on (delete it). This analyzer reports
// isolated nodes — zero edges of *any* kind, structural edges
// included — which a normally extracted symbol never has. An isolated
// node signals the indexer mis-extracted the symbol or its file, not
// that the code is unused; the response carries a `note` spelling out
// the distinction so a reader does not delete live code.
package mcp
import (
"context"
"fmt"
"strings"
"github.com/mark3labs/mcp-go/mcp"
"github.com/zzet/gortex/internal/analysis"
)
// handleAnalyzeConnectivityHealth answers analyze kind=connectivity_health.
//
// It walks the session-scoped node set once, classifying each node via
// the shared graph.ClassifyZeroEdge (isolated == zero edges of any
// kind) and by in/out degree (leaf / source-only / sink-only), then
// rolls the isolated + leaf counts up per node kind and per source
// file.
//
// Args:
//
// - `limit`: cap the dead_weight_by_file ranking (default 50).
func (s *Server) handleAnalyzeConnectivityHealth(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
args := req.GetArguments()
fileLimit := 50
if v, ok := args["limit"].(float64); ok && v > 0 {
fileLimit = int(v)
}
report := analysis.GraphConnectivity(s.graph, s.scopedNodes(ctx), fileLimit)
if s.isGCX(ctx, req) {
return s.gcxResponseWithBudget(req)(encodeAnalyze("connectivity_health", report))
}
if isCompact(req) {
var b strings.Builder
fmt.Fprintf(&b, "effective %d / nominal %d (ratio %.3f)\n",
report.EffectiveNodes, report.NominalNodes, report.EffectiveRatio)
fmt.Fprintf(&b, "isolated %d leaf %d source_only %d sink_only %d\n",
report.Isolated, report.Leaf, report.SourceOnly, report.SinkOnly)
for _, k := range report.ByKind {
if k.Isolated == 0 && k.Leaf == 0 {
continue
}
fmt.Fprintf(&b, " %-12s total %-5d isolated %-4d leaf %d\n",
k.Kind, k.Total, k.Isolated, k.Leaf)
}
for _, f := range report.DeadWeightByFile {
fmt.Fprintf(&b, " %3d dead-weight %s (isolated %d, leaf %d)\n",
f.DeadWeight, f.FilePath, f.Isolated, f.Leaf)
}
b.WriteString("\nnote: " + report.Note + "\n")
return mcp.NewToolResultText(b.String()), nil
}
return s.respondJSONOrTOON(ctx, req, report)
}