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
103 lines
3.8 KiB
Go
103 lines
3.8 KiB
Go
package mcp
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/mark3labs/mcp-go/mcp"
|
|
)
|
|
|
|
// hostContext is a runtime, per-host adaptation of the served tool
|
|
// surface, resolved from the MCP initialize clientInfo.name. It is the
|
|
// serve-time counterpart of the install-time agent adapters: it can hide
|
|
// tools the host duplicates, override individual tool descriptions, and
|
|
// carry a host-specific guidance fragment surfaced via tool_profile.
|
|
type hostContext struct {
|
|
name string // canonical host name ("" = no context)
|
|
matches []string // lowercase substrings of clientInfo.name that select this context
|
|
instruction string // host-specific guidance fragment
|
|
excluded map[string]bool // tools removed from this host's tools/list
|
|
descOverride map[string]string // per-tool description replacements
|
|
}
|
|
|
|
// empty reports whether this context applies no adaptation at all.
|
|
func (h hostContext) empty() bool {
|
|
return h.name == "" && len(h.excluded) == 0 && len(h.descOverride) == 0
|
|
}
|
|
|
|
// apply returns tools with this context's exclusions removed and its
|
|
// description overrides applied. The input slice and its elements are
|
|
// not mutated.
|
|
func (h hostContext) apply(tools []mcp.Tool) []mcp.Tool {
|
|
if len(h.excluded) == 0 && len(h.descOverride) == 0 {
|
|
return tools
|
|
}
|
|
out := make([]mcp.Tool, 0, len(tools))
|
|
for _, t := range tools {
|
|
if h.excluded[t.Name] {
|
|
continue
|
|
}
|
|
if ov, ok := h.descOverride[t.Name]; ok {
|
|
t.Description = ov
|
|
}
|
|
out = append(out, t)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// editorHostInstruction is shared by the IDE-extension hosts.
|
|
const editorHostInstruction = "You are driving Gortex from an editor extension. Push unsaved buffers " +
|
|
"with overlay_push so graph queries see your in-progress edits before they reach disk, and use " +
|
|
"preview_edit / simulate_chain to evaluate a change without writing it."
|
|
|
|
// hostContexts is the runtime registry of per-host adaptations, matched
|
|
// against the MCP initialize clientInfo.name. Order matters — the first
|
|
// matching entry wins, so more specific hosts come first.
|
|
var hostContexts = []hostContext{
|
|
{
|
|
name: "claude-code",
|
|
matches: []string{"claude"},
|
|
instruction: "Gortex runs here with PreToolUse hooks that redirect Read / Grep / Glob to graph " +
|
|
"tools. Begin every task with smart_context, prefer get_symbol_source over reading whole " +
|
|
"files, and edit through edit_file / edit_symbol.",
|
|
},
|
|
{name: "cursor", matches: []string{"cursor"}, instruction: editorHostInstruction},
|
|
{name: "vscode", matches: []string{"vscode", "visual studio"}, instruction: editorHostInstruction},
|
|
{name: "zed", matches: []string{"zed"}, instruction: editorHostInstruction},
|
|
{name: "windsurf", matches: []string{"windsurf"}, instruction: editorHostInstruction},
|
|
{name: "jetbrains", matches: []string{"jetbrains", "intellij"}, instruction: editorHostInstruction},
|
|
{
|
|
name: "codex",
|
|
matches: []string{"codex"},
|
|
instruction: "Gortex is available as an MCP server. Use search_symbols and smart_context to " +
|
|
"locate code before editing, and verify changes with check_guards and get_test_targets.",
|
|
},
|
|
}
|
|
|
|
// resolveHostContext returns the hostContext matching clientName, or the
|
|
// zero context (no adaptation) when the host is unknown or unidentified.
|
|
func resolveHostContext(clientName string) hostContext {
|
|
name := strings.ToLower(strings.TrimSpace(clientName))
|
|
if name == "" {
|
|
return hostContext{}
|
|
}
|
|
for _, hc := range hostContexts {
|
|
for _, m := range hc.matches {
|
|
if strings.Contains(name, m) {
|
|
return hc
|
|
}
|
|
}
|
|
}
|
|
return hostContext{}
|
|
}
|
|
|
|
// sessionHostContext resolves the per-host context for the request's
|
|
// session from the MCP client name captured at initialize time.
|
|
func (s *Server) sessionHostContext(ctx context.Context) hostContext {
|
|
sess := s.sessionFor(ctx)
|
|
if sess == nil {
|
|
return hostContext{}
|
|
}
|
|
return resolveHostContext(sess.snapshotClientName())
|
|
}
|