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
65 lines
2.1 KiB
Go
65 lines
2.1 KiB
Go
package mcp
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/zzet/gortex/internal/graph"
|
|
"github.com/zzet/gortex/internal/query"
|
|
)
|
|
|
|
// Proactive deferred-tool surfacing: the measured failure is that agents
|
|
// consult tools_search once and never again, so a deferred tool is
|
|
// effectively invisible. When a response's CONTENT matches a deferred
|
|
// tool's trigger, we append one compact, additive `related_tools` cue —
|
|
// once per tool per session, at most one per response — in the same style
|
|
// as the existing completeness cues (SuppressionCaveat). Pure rules on
|
|
// response shape; no LLM calls, no other response content changes.
|
|
|
|
// dispatchImplementorCount counts the concrete implementors / overriders of
|
|
// a symbol from the resolved graph — the signal that a plain find_usages on
|
|
// this symbol is under-selling the picture because dispatch fans out to
|
|
// implementations the reference list doesn't name.
|
|
func (s *Server) dispatchImplementorCount(id string) int {
|
|
if s == nil || s.graph == nil || id == "" {
|
|
return 0
|
|
}
|
|
n := 0
|
|
for _, e := range s.graph.GetInEdges(id) {
|
|
if e == nil {
|
|
continue
|
|
}
|
|
if e.Kind == graph.EdgeImplements || e.Kind == graph.EdgeOverrides {
|
|
n++
|
|
}
|
|
}
|
|
return n
|
|
}
|
|
|
|
// attachRelatedToolsCue attaches a related_tools cue to a find_usages
|
|
// subgraph when the target is dispatch-heavy (an interface / base method
|
|
// with concrete implementors), pointing at find_implementations — the tool
|
|
// that names those implementors a reference list cannot. Once per session.
|
|
func (s *Server) attachRelatedToolsCue(ctx context.Context, sg *query.SubGraph, id string) {
|
|
if sg == nil || sg.RelatedTools != "" {
|
|
return
|
|
}
|
|
// Don't compete with the zero-edge / tier-filtered caveats — those own
|
|
// the "why is this empty" story.
|
|
if sg.Caveat != nil || sg.TierFiltered != nil {
|
|
return
|
|
}
|
|
const minImplementors = 2
|
|
n := s.dispatchImplementorCount(id)
|
|
if n < minImplementors {
|
|
return
|
|
}
|
|
sess := s.sessionFor(ctx)
|
|
if sess == nil || !sess.markCueOnce("find_implementations") {
|
|
return
|
|
}
|
|
sg.RelatedTools = fmt.Sprintf(
|
|
"target has %d implementations/overrides; find_implementations lists the concrete ones dispatch reaches",
|
|
n)
|
|
}
|