chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/github/github-mcp-server/pkg/ifc"
|
||||
"github.com/google/go-github/v89/github"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
)
|
||||
|
||||
// setIFCLabel writes the given IFC security label into a tool result's _meta
|
||||
// under the "ifc" key, allocating the Meta map if necessary.
|
||||
func setIFCLabel(r *mcp.CallToolResult, label ifc.SecurityLabel) {
|
||||
if r.Meta == nil {
|
||||
r.Meta = mcp.Meta{}
|
||||
}
|
||||
r.Meta["ifc"] = label
|
||||
}
|
||||
|
||||
func shouldAttachIFCLabel(ctx context.Context, deps ToolDependencies, r *mcp.CallToolResult) bool {
|
||||
return r != nil && !r.IsError && deps.IsFeatureEnabled(ctx, FeatureFlagIFCLabels)
|
||||
}
|
||||
|
||||
// attachStaticIFCLabel attaches a fixed IFC label to a successful tool result
|
||||
// when IFC labels are enabled. It is used by tools whose label does not depend
|
||||
// on any repository visibility lookup (e.g. security alerts, global
|
||||
// advisories, team membership, notification subjects).
|
||||
//
|
||||
// Error results are left untouched, and the label is omitted entirely when the
|
||||
// IFC feature flag is disabled.
|
||||
func attachStaticIFCLabel(ctx context.Context, deps ToolDependencies, r *mcp.CallToolResult, label ifc.SecurityLabel) *mcp.CallToolResult {
|
||||
if !shouldAttachIFCLabel(ctx, deps, r) {
|
||||
return r
|
||||
}
|
||||
setIFCLabel(r, label)
|
||||
return r
|
||||
}
|
||||
|
||||
// attachRepoVisibilityIFCLabel attaches an IFC label derived from a single
|
||||
// repository's visibility to a successful tool result when IFC labels are
|
||||
// enabled. The concrete label is produced by labelFn, which receives whether
|
||||
// the repository is private.
|
||||
//
|
||||
// The repository visibility is resolved via FetchRepoIsPrivate. Consistent
|
||||
// with the other IFC-labeled tools, if the visibility lookup fails the label
|
||||
// is omitted rather than risking a misclassification. Error results and the
|
||||
// disabled-feature case are left untouched.
|
||||
func attachRepoVisibilityIFCLabel(
|
||||
ctx context.Context,
|
||||
deps ToolDependencies,
|
||||
client *github.Client,
|
||||
owner, repo string,
|
||||
r *mcp.CallToolResult,
|
||||
labelFn func(isPrivate bool) ifc.SecurityLabel,
|
||||
) *mcp.CallToolResult {
|
||||
if !shouldAttachIFCLabel(ctx, deps, r) {
|
||||
return r
|
||||
}
|
||||
isPrivate, err := FetchRepoIsPrivate(ctx, client, owner, repo)
|
||||
if err != nil {
|
||||
return r
|
||||
}
|
||||
setIFCLabel(r, labelFn(isPrivate))
|
||||
return r
|
||||
}
|
||||
|
||||
// ifcSearchPostProcessOption returns a searchOption that attaches IFC labels to
|
||||
// a multi-repository search result. The feature-flag check is centralized here
|
||||
// (mirroring the attach* helpers above) rather than in each search tool
|
||||
// handler: when IFC labels are disabled it returns a no-op option, so callers
|
||||
// can pass it unconditionally to searchHandler.
|
||||
func ifcSearchPostProcessOption(ctx context.Context, deps ToolDependencies) searchOption {
|
||||
if !deps.IsFeatureEnabled(ctx, FeatureFlagIFCLabels) {
|
||||
return func(*searchConfig) {}
|
||||
}
|
||||
return withSearchPostProcess(searchIssuesIFCPostProcess(deps))
|
||||
}
|
||||
|
||||
// attachRepoVisibilityIFCLabelLazy is like attachRepoVisibilityIFCLabel but
|
||||
// resolves the REST client itself, only when IFC labels are enabled. It is used
|
||||
// by tools whose handler holds a GraphQL client (or no client yet) and would
|
||||
// otherwise have to acquire a REST client solely to compute the label. The
|
||||
// feature-flag check is centralized here so callers can invoke it
|
||||
// unconditionally; if the client cannot be obtained or the visibility lookup
|
||||
// fails, the label is omitted rather than risking a misclassification.
|
||||
func attachRepoVisibilityIFCLabelLazy(
|
||||
ctx context.Context,
|
||||
deps ToolDependencies,
|
||||
owner, repo string,
|
||||
r *mcp.CallToolResult,
|
||||
labelFn func(isPrivate bool) ifc.SecurityLabel,
|
||||
) *mcp.CallToolResult {
|
||||
if !shouldAttachIFCLabel(ctx, deps, r) {
|
||||
return r
|
||||
}
|
||||
client, err := deps.GetClient(ctx)
|
||||
if err != nil {
|
||||
return r
|
||||
}
|
||||
return attachRepoVisibilityIFCLabel(ctx, deps, client, owner, repo, r, labelFn)
|
||||
}
|
||||
|
||||
// attachJoinedIFCLabel attaches an IFC label computed by joining a set of
|
||||
// per-item visibilities (true == private) when IFC labels are enabled. joinFn
|
||||
// is the lattice join for the relevant item kind (e.g. ifc.LabelSearchIssues or
|
||||
// ifc.LabelProjectList). The visibility slice is cheap to build from an
|
||||
// already-fetched response, so callers may construct it unconditionally and let
|
||||
// this helper own the feature-flag gate.
|
||||
func attachJoinedIFCLabel(
|
||||
ctx context.Context,
|
||||
deps ToolDependencies,
|
||||
r *mcp.CallToolResult,
|
||||
visibilities []bool,
|
||||
joinFn func([]bool) ifc.SecurityLabel,
|
||||
) *mcp.CallToolResult {
|
||||
if !shouldAttachIFCLabel(ctx, deps, r) {
|
||||
return r
|
||||
}
|
||||
setIFCLabel(r, joinFn(visibilities))
|
||||
return r
|
||||
}
|
||||
|
||||
func attachProjectVisibilityIFCLabel(
|
||||
ctx context.Context,
|
||||
deps ToolDependencies,
|
||||
r *mcp.CallToolResult,
|
||||
isPrivate bool,
|
||||
labelFn func(isPrivate bool) ifc.SecurityLabel,
|
||||
) *mcp.CallToolResult {
|
||||
if !shouldAttachIFCLabel(ctx, deps, r) {
|
||||
return r
|
||||
}
|
||||
setIFCLabel(r, labelFn(isPrivate))
|
||||
return r
|
||||
}
|
||||
|
||||
// newRepoVisibilityIFCLabeler returns a closure that attaches a repo-visibility
|
||||
// IFC label to a tool result, for handlers that have several return paths and
|
||||
// want to label each one. The returned function owns the feature-flag gate (so
|
||||
// callers invoke it unconditionally) and caches the repository visibility
|
||||
// lookup across calls, so a handler that returns from many branches only pays
|
||||
// for one FetchRepoIsPrivate call. A failed visibility lookup is not cached, so
|
||||
// a later return path can retry; on persistent failure the label is omitted
|
||||
// rather than risking a misclassification.
|
||||
func newRepoVisibilityIFCLabeler(
|
||||
ctx context.Context,
|
||||
deps ToolDependencies,
|
||||
client *github.Client,
|
||||
owner, repo string,
|
||||
labelFn func(isPrivate bool) ifc.SecurityLabel,
|
||||
) func(*mcp.CallToolResult) *mcp.CallToolResult {
|
||||
var (
|
||||
known bool
|
||||
isPrivate bool
|
||||
)
|
||||
return func(r *mcp.CallToolResult) *mcp.CallToolResult {
|
||||
if r == nil || r.IsError || !deps.IsFeatureEnabled(ctx, FeatureFlagIFCLabels) {
|
||||
return r
|
||||
}
|
||||
if !known {
|
||||
p, err := FetchRepoIsPrivate(ctx, client, owner, repo)
|
||||
if err != nil {
|
||||
return r
|
||||
}
|
||||
isPrivate = p
|
||||
known = true
|
||||
}
|
||||
setIFCLabel(r, labelFn(isPrivate))
|
||||
return r
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user