Files
zzet--gortex/internal/mcp/tools_analyze_resolution_outcomes.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

79 lines
2.7 KiB
Go

package mcp
import (
"context"
"sort"
"strconv"
"strings"
"github.com/mark3labs/mcp-go/mcp"
"github.com/zzet/gortex/internal/analyzer"
"github.com/zzet/gortex/internal/graph"
)
// Structured resolver-suppression taxonomy. The canonical constants live in
// internal/analyzer; these aliases keep existing MCP-package call sites and
// tests compiling against a single source of truth.
const (
outcomeAmbiguousMultiMatch = analyzer.OutcomeAmbiguousMultiMatch
outcomeCandidateOutOfScope = analyzer.OutcomeCandidateOutOfScope
outcomeCrossLanguageOnly = analyzer.OutcomeCrossLanguageOnly
outcomeNoDefinition = analyzer.OutcomeNoDefinition
outcomeStdlibHeader = analyzer.OutcomeStdlibHeader
)
// handleAnalyzeResolutionOutcomes classifies every unresolved call /
// reference edge by the structured reason the resolver gave up, and
// returns a per-reason rollup plus example rows. Optional `reason`
// filters to one outcome; optional `limit` caps the example rows.
//
// The classification itself lives in
// internal/analyzer.AnalyzeResolutionOutcomes — a pure Calculation — so the
// same taxonomy logic is independently testable and reusable across surfaces.
func (s *Server) handleAnalyzeResolutionOutcomes(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
args := req.GetArguments()
reasonFilter := strings.TrimSpace(stringArg(args, "reason"))
limit := intArg(args, "limit", 50)
result := analyzer.AnalyzeResolutionOutcomes(s.graph, reasonFilter, limit)
if isCompact(req) {
var b strings.Builder
reasons := make([]string, 0, len(result.ByReason))
for r := range result.ByReason {
reasons = append(reasons, r)
}
sort.Slice(reasons, func(i, j int) bool { return result.ByReason[reasons[i]] > result.ByReason[reasons[j]] })
for _, r := range reasons {
b.WriteString(r)
b.WriteString(": ")
b.WriteString(strconv.Itoa(result.ByReason[r]))
b.WriteByte('\n')
}
if len(result.ByReason) == 0 {
b.WriteString("no unresolved edges\n")
}
return mcp.NewToolResultText(b.String()), nil
}
return s.respondJSONOrTOON(ctx, req, map[string]any{
"by_reason": result.ByReason,
"total": result.Total,
"rows": result.Rows,
})
}
// nodeIsDefinitionKind reports whether a node kind is a callable / type
// definition an unresolved call or reference could legitimately bind to.
// The resolution-outcome classifier itself now lives in internal/analyzer;
// this helper stays in the mcp package because id_resolve.go also relies on it.
func nodeIsDefinitionKind(k graph.NodeKind) bool {
switch k {
case graph.KindFunction, graph.KindMethod, graph.KindType,
graph.KindInterface, graph.KindVariable, graph.KindConstant, graph.KindField:
return true
}
return false
}