Documentation
¶
Overview ¶
Package provisioner detects installed LLM clients and manages their MCP gateway configuration, enabling zero-friction connection between gridctl and tools like Claude Desktop, Cursor, VS Code, and others.
Index ¶
- Constants
- Variables
- func AppendClientParam(rawURL, clientID string) string
- func CreateBackup(path string) (string, error)
- func DryRunDiff(configPath string, prov ClientProvisioner, opts LinkOptions) (before, after string, err error)
- func GatewayHTTPURL(port int) string
- func GatewayURL(port int) string
- func GroupGatewayHTTPURL(port int, group string) string
- func GroupGatewayURL(port int, group string) string
- func HasComments(configPath string) bool
- func LooksLikeLegacyLink(raw map[string]any, needsBridge bool) bool
- func PlannedEntry(prov ClientProvisioner, opts LinkOptions) map[string]any
- func PostLinkNotesFor(prov ClientProvisioner) []string
- func ProjectWalk(ctx context.Context, startDir string) ([]string, error)
- func TransportDescription(needsBridge bool) string
- func TransportDescriptionFor(prov ClientProvisioner) string
- type Antigravity
- func (c *Antigravity) Detect() (string, bool)
- func (p *Antigravity) IsLinked(configPath string, serverName string) (bool, error)
- func (p *Antigravity) Link(configPath string, opts LinkOptions) error
- func (p *Antigravity) ListServers(configPath string) ([]ServerEntry, error)
- func (p *Antigravity) Name() string
- func (p *Antigravity) NeedsBridge() bool
- func (p *Antigravity) PlannedEntry(opts LinkOptions) map[string]any
- func (p *Antigravity) Slug() string
- func (p *Antigravity) Unlink(configPath string, serverName string) error
- type AnythingLLM
- func (p *AnythingLLM) Detect() (string, bool)
- func (p *AnythingLLM) IsLinked(configPath string, serverName string) (bool, error)
- func (p *AnythingLLM) Link(configPath string, opts LinkOptions) error
- func (p *AnythingLLM) ListServers(configPath string) ([]ServerEntry, error)
- func (p *AnythingLLM) Name() string
- func (p *AnythingLLM) NeedsBridge() bool
- func (p *AnythingLLM) PlannedEntry(opts LinkOptions) map[string]any
- func (p *AnythingLLM) Slug() string
- func (p *AnythingLLM) Unlink(configPath string, serverName string) error
- type ClaudeCode
- func (c *ClaudeCode) Detect() (string, bool)
- func (p *ClaudeCode) IsLinked(configPath string, serverName string) (bool, error)
- func (p *ClaudeCode) Link(configPath string, opts LinkOptions) error
- func (p *ClaudeCode) ListServers(configPath string) ([]ServerEntry, error)
- func (p *ClaudeCode) Name() string
- func (p *ClaudeCode) NeedsBridge() bool
- func (p *ClaudeCode) PlannedEntry(opts LinkOptions) map[string]any
- func (p *ClaudeCode) Slug() string
- func (p *ClaudeCode) Unlink(configPath string, serverName string) error
- type ClaudeDesktop
- func (p *ClaudeDesktop) Detect() (string, bool)
- func (p *ClaudeDesktop) IsLinked(configPath string, serverName string) (bool, error)
- func (p *ClaudeDesktop) Link(configPath string, opts LinkOptions) error
- func (p *ClaudeDesktop) ListServers(configPath string) ([]ServerEntry, error)
- func (p *ClaudeDesktop) Name() string
- func (p *ClaudeDesktop) NeedsBridge() bool
- func (p *ClaudeDesktop) PlannedEntry(opts LinkOptions) map[string]any
- func (p *ClaudeDesktop) Slug() string
- func (p *ClaudeDesktop) Unlink(configPath string, serverName string) error
- type ClientInfo
- type ClientProvisioner
- type Cline
- func (p *Cline) Detect() (string, bool)
- func (p *Cline) IsLinked(configPath string, serverName string) (bool, error)
- func (p *Cline) Link(configPath string, opts LinkOptions) error
- func (p *Cline) ListServers(configPath string) ([]ServerEntry, error)
- func (p *Cline) Name() string
- func (p *Cline) NeedsBridge() bool
- func (p *Cline) PlannedEntry(opts LinkOptions) map[string]any
- func (p *Cline) Slug() string
- func (p *Cline) Unlink(configPath string, serverName string) error
- type ContinueDev
- func (c *ContinueDev) Detect() (string, bool)
- func (c *ContinueDev) IsLinked(configPath string, serverName string) (bool, error)
- func (c *ContinueDev) Link(configPath string, opts LinkOptions) error
- func (c *ContinueDev) ListServers(configPath string) ([]ServerEntry, error)
- func (c *ContinueDev) Name() string
- func (c *ContinueDev) NeedsBridge() bool
- func (c *ContinueDev) PlannedEntry(opts LinkOptions) map[string]any
- func (c *ContinueDev) Slug() string
- func (c *ContinueDev) Unlink(configPath string, serverName string) error
- type Cursor
- func (p *Cursor) Detect() (string, bool)
- func (p *Cursor) IsLinked(configPath string, serverName string) (bool, error)
- func (p *Cursor) Link(configPath string, opts LinkOptions) error
- func (p *Cursor) ListServers(configPath string) ([]ServerEntry, error)
- func (p *Cursor) Name() string
- func (p *Cursor) NeedsBridge() bool
- func (p *Cursor) PlannedEntry(opts LinkOptions) map[string]any
- func (p *Cursor) Slug() string
- func (p *Cursor) Unlink(configPath string, serverName string) error
- type DetectedClient
- type EntryPlanner
- type GeminiCLI
- func (p *GeminiCLI) Detect() (string, bool)
- func (p *GeminiCLI) IsLinked(configPath string, serverName string) (bool, error)
- func (p *GeminiCLI) Link(configPath string, opts LinkOptions) error
- func (p *GeminiCLI) ListServers(configPath string) ([]ServerEntry, error)
- func (p *GeminiCLI) Name() string
- func (p *GeminiCLI) NeedsBridge() bool
- func (p *GeminiCLI) PlannedEntry(opts LinkOptions) map[string]any
- func (p *GeminiCLI) Slug() string
- func (p *GeminiCLI) Unlink(configPath string, serverName string) error
- type Goose
- func (g *Goose) Detect() (string, bool)
- func (g *Goose) IsLinked(configPath string, serverName string) (bool, error)
- func (g *Goose) Link(configPath string, opts LinkOptions) error
- func (g *Goose) ListServers(configPath string) ([]ServerEntry, error)
- func (g *Goose) Name() string
- func (g *Goose) NeedsBridge() bool
- func (g *Goose) PlannedEntry(opts LinkOptions) map[string]any
- func (g *Goose) Slug() string
- func (g *Goose) Unlink(configPath string, serverName string) error
- type GrokBuild
- func (g *GrokBuild) Detect() (string, bool)
- func (g *GrokBuild) IsLinked(configPath string, serverName string) (bool, error)
- func (g *GrokBuild) Link(configPath string, opts LinkOptions) error
- func (g *GrokBuild) ListServers(configPath string) ([]ServerEntry, error)
- func (g *GrokBuild) Name() string
- func (g *GrokBuild) NeedsBridge() bool
- func (g *GrokBuild) PlannedEntry(opts LinkOptions) map[string]any
- func (g *GrokBuild) Slug() string
- func (g *GrokBuild) Unlink(configPath string, serverName string) error
- type ImportRead
- type LMStudio
- func (p *LMStudio) Detect() (string, bool)
- func (p *LMStudio) IsLinked(configPath string, serverName string) (bool, error)
- func (p *LMStudio) Link(configPath string, opts LinkOptions) error
- func (p *LMStudio) ListServers(configPath string) ([]ServerEntry, error)
- func (p *LMStudio) Name() string
- func (p *LMStudio) NeedsBridge() bool
- func (p *LMStudio) PlannedEntry(opts LinkOptions) map[string]any
- func (c *LMStudio) PostLinkNotes() []string
- func (p *LMStudio) Slug() string
- func (p *LMStudio) Unlink(configPath string, serverName string) error
- type LinkOptions
- type LinkResult
- type LocalScopeEntries
- type OpenCode
- func (o *OpenCode) Detect() (string, bool)
- func (o *OpenCode) IsLinked(configPath string, serverName string) (bool, error)
- func (o *OpenCode) Link(configPath string, opts LinkOptions) error
- func (o *OpenCode) ListServers(configPath string) ([]ServerEntry, error)
- func (o *OpenCode) Name() string
- func (o *OpenCode) NeedsBridge() bool
- func (o *OpenCode) PlannedEntry(opts LinkOptions) map[string]any
- func (o *OpenCode) Slug() string
- func (o *OpenCode) Unlink(configPath string, serverName string) error
- type OpenCodeImportRead
- type OpenCodeImportSelection
- type PostLinkNoter
- type ProjectSource
- type ProjectSourceFile
- type Registry
- type RooCode
- func (p *RooCode) Detect() (string, bool)
- func (p *RooCode) IsLinked(configPath string, serverName string) (bool, error)
- func (p *RooCode) Link(configPath string, opts LinkOptions) error
- func (p *RooCode) ListServers(configPath string) ([]ServerEntry, error)
- func (p *RooCode) Name() string
- func (p *RooCode) NeedsBridge() bool
- func (p *RooCode) PlannedEntry(opts LinkOptions) map[string]any
- func (p *RooCode) Slug() string
- func (p *RooCode) Unlink(configPath string, serverName string) error
- type ServerEntry
- type VSCode
- func (v *VSCode) Detect() (string, bool)
- func (v *VSCode) IsLinked(configPath string, serverName string) (bool, error)
- func (v *VSCode) Link(configPath string, opts LinkOptions) error
- func (v *VSCode) ListServers(configPath string) ([]ServerEntry, error)
- func (v *VSCode) Name() string
- func (v *VSCode) NeedsBridge() bool
- func (v *VSCode) PlannedEntry(opts LinkOptions) map[string]any
- func (v *VSCode) Slug() string
- func (v *VSCode) Unlink(configPath string, serverName string) error
- type Windsurf
- func (p *Windsurf) Detect() (string, bool)
- func (p *Windsurf) IsLinked(configPath string, serverName string) (bool, error)
- func (p *Windsurf) Link(configPath string, opts LinkOptions) error
- func (p *Windsurf) ListServers(configPath string) ([]ServerEntry, error)
- func (p *Windsurf) Name() string
- func (p *Windsurf) NeedsBridge() bool
- func (p *Windsurf) PlannedEntry(opts LinkOptions) map[string]any
- func (p *Windsurf) Slug() string
- func (p *Windsurf) Unlink(configPath string, serverName string) error
- type Zed
- func (z *Zed) Detect() (string, bool)
- func (z *Zed) IsLinked(configPath string, serverName string) (bool, error)
- func (z *Zed) Link(configPath string, opts LinkOptions) error
- func (z *Zed) ListServers(configPath string) ([]ServerEntry, error)
- func (z *Zed) Name() string
- func (z *Zed) NeedsBridge() bool
- func (z *Zed) PlannedEntry(opts LinkOptions) map[string]any
- func (z *Zed) Slug() string
- func (z *Zed) Unlink(configPath string, serverName string) error
Constants ¶
const ( OpenCodeImportSelected = "selected" OpenCodeImportMissing = "missing" OpenCodeImportUnreadable = "unreadable" OpenCodeImportMalformed = "malformed" OpenCodeImportEmpty = "empty" )
OpenCode import file statuses. They describe the selected file only. A failed read does not fall through to another file.
const OpenCodeImportPolicy = "" /* 252-byte string literal not displayed */
OpenCodeImportPolicy explains why import reads one file instead of OpenCode's merged effective configuration.
Variables ¶
var ( ErrAlreadyLinked = errors.New("already linked with identical config") ErrConflict = errors.New("existing entry has unexpected config") ErrNotLinked = errors.New("no gridctl entry found") ErrClientNotFound = errors.New("client not detected on this system") ErrNpxNotFound = errors.New("npx not found in PATH") )
Sentinel errors for link operations.
var NpxAvailable = func() bool { _, err := exec.LookPath("npx") return err == nil }
NpxAvailable checks if npx is available in PATH. Exported as a variable to allow test overrides.
Functions ¶
func AppendClientParam ¶
AppendClientParam adds (or replaces) the `client` query parameter on a gateway URL so the gateway can resolve the connecting client's stable access identifier from the wire. Returns the URL unchanged when clientID is empty or the URL cannot be parsed.
func CreateBackup ¶
CreateBackup exposes the timestamped-backup helper (".gridctl-backup-<ts>", pruned to the most recent three) for callers outside the package. The import command uses it on stack.yaml before appending, so the one backup convention covers both directions of config mutation.
func DryRunDiff ¶
func DryRunDiff(configPath string, prov ClientProvisioner, opts LinkOptions) (before, after string, err error)
DryRunDiff returns the before/after config for a dry run. Supports both JSON and YAML formats based on the provisioner type.
func GatewayHTTPURL ¶
GatewayHTTPURL constructs the streamable HTTP gateway URL from a port.
func GatewayURL ¶
GatewayURL constructs the legacy SSE gateway URL from a port. New links never write this shape (legacy HTTP+SSE is deprecated upstream); it exists so existing /sse entries are still recognized on read.
func GroupGatewayHTTPURL ¶
GroupGatewayHTTPURL constructs a tool group's streamable HTTP gateway URL.
func GroupGatewayURL ¶
GroupGatewayURL constructs a tool group's legacy SSE gateway URL. Like GatewayURL, it is read-side only: new links write the HTTP shape.
func HasComments ¶
HasComments checks if a config file contains comments that would be lost on write. The comment syntax depends on the file format: TOML and YAML use '#' line comments, while JSON/JSONC uses '//' and '/*'. Returns false if the file doesn't exist.
func LooksLikeLegacyLink ¶
LooksLikeLegacyLink reports whether an unrecorded entry is plausibly a gridctl link written before ownership recording existed. Per Article XVI this heuristic assists one-time migration only: it feeds the "likely a pre-lockfile link; adopt it" remediation hint and is never a steady-state ownership check. It recognizes every shape gridctl has written: localhost URLs under the known URL keys (flat or nested in a Continue-style transport object) and npx-launched mcp-remote bridges.
func PlannedEntry ¶
func PlannedEntry(prov ClientProvisioner, opts LinkOptions) map[string]any
PlannedEntry returns the entry value prov's Link would write for opts, or nil when the provisioner cannot plan (every registered client can).
func PostLinkNotesFor ¶
func PostLinkNotesFor(prov ClientProvisioner) []string
PostLinkNotesFor returns the provisioner's post-link notes, or nil when it declares none.
func ProjectWalk ¶
ProjectWalk returns the directories project discovery visits, nearest first. The walk stops at the first git root, inclusive. When no directory in the chain is a git root, only startDir is returned. It does not read file contents and does not use the Gridctl home.
func TransportDescription ¶
TransportDescription returns a human-readable transport description.
func TransportDescriptionFor ¶
func TransportDescriptionFor(prov ClientProvisioner) string
TransportDescriptionFor returns a transport description for a specific provisioner. Every non-bridge client now links over streamable HTTP.
Types ¶
type Antigravity ¶
type Antigravity struct {
// contains filtered or unexported fields
}
Antigravity provisions the Google Antigravity IDE MCP config. Transport: native streamable HTTP (no bridge needed). Antigravity borrows Windsurf's `serverUrl` field name but speaks Streamable HTTP, so the entry points at the gateway's /mcp endpoint rather than /sse.
func (*Antigravity) Detect ¶
func (c *Antigravity) Detect() (string, bool)
Detect prefers the Antigravity 2.0 shared config path, falling back to the pre-2.0 IDE path so installs that predate the migration still link.
func (*Antigravity) Link ¶
func (p *Antigravity) Link(configPath string, opts LinkOptions) error
func (*Antigravity) ListServers ¶
func (p *Antigravity) ListServers(configPath string) ([]ServerEntry, error)
ListServers implements the shared read-only enumeration for standard { "mcpServers": { name: {...} } } clients.
func (*Antigravity) NeedsBridge ¶
func (p *Antigravity) NeedsBridge() bool
func (*Antigravity) PlannedEntry ¶
func (p *Antigravity) PlannedEntry(opts LinkOptions) map[string]any
PlannedEntry implements EntryPlanner for the shared mcpServers clients, including the per-client extra keys Link merges in.
type AnythingLLM ¶
type AnythingLLM struct {
// contains filtered or unexported fields
}
AnythingLLM provisions the AnythingLLM desktop MCP config. Transport: native streamable HTTP (no bridge needed); AnythingLLM's token is "streamable" ("streamable-http" is rejected, and omitting type means SSE). Config uses standard { "mcpServers": { "name": {...} } } structure.
func (*AnythingLLM) Link ¶
func (p *AnythingLLM) Link(configPath string, opts LinkOptions) error
func (*AnythingLLM) ListServers ¶
func (p *AnythingLLM) ListServers(configPath string) ([]ServerEntry, error)
ListServers implements the shared read-only enumeration for standard { "mcpServers": { name: {...} } } clients.
func (*AnythingLLM) NeedsBridge ¶
func (p *AnythingLLM) NeedsBridge() bool
func (*AnythingLLM) PlannedEntry ¶
func (p *AnythingLLM) PlannedEntry(opts LinkOptions) map[string]any
PlannedEntry implements EntryPlanner for the shared mcpServers clients, including the per-client extra keys Link merges in.
type ClaudeCode ¶
type ClaudeCode struct {
// contains filtered or unexported fields
}
ClaudeCode provisions the Claude Code CLI MCP config. Transport: native HTTP (no bridge needed).
func (*ClaudeCode) Detect ¶
func (c *ClaudeCode) Detect() (string, bool)
Detect overrides mcpServersProvisioner.Detect() because the config file ~/.claude.json has ~ as its parent, which always exists. Instead, check for the ~/.claude/ directory as the installation indicator. When CLAUDE_CONFIG_DIR points at an existing config file, that file wins over the home-directory default, matching Claude Code's own resolution order.
func (*ClaudeCode) Link ¶
func (p *ClaudeCode) Link(configPath string, opts LinkOptions) error
func (*ClaudeCode) ListServers ¶
func (p *ClaudeCode) ListServers(configPath string) ([]ServerEntry, error)
ListServers implements the shared read-only enumeration for standard { "mcpServers": { name: {...} } } clients.
func (*ClaudeCode) NeedsBridge ¶
func (p *ClaudeCode) NeedsBridge() bool
func (*ClaudeCode) PlannedEntry ¶
func (p *ClaudeCode) PlannedEntry(opts LinkOptions) map[string]any
PlannedEntry implements EntryPlanner for the shared mcpServers clients, including the per-client extra keys Link merges in.
type ClaudeDesktop ¶
type ClaudeDesktop struct {
// contains filtered or unexported fields
}
ClaudeDesktop provisions the Claude Desktop MCP config. Transport: stdio only (requires mcp-remote bridge).
func (*ClaudeDesktop) Link ¶
func (p *ClaudeDesktop) Link(configPath string, opts LinkOptions) error
func (*ClaudeDesktop) ListServers ¶
func (p *ClaudeDesktop) ListServers(configPath string) ([]ServerEntry, error)
ListServers implements the shared read-only enumeration for standard { "mcpServers": { name: {...} } } clients.
func (*ClaudeDesktop) NeedsBridge ¶
func (p *ClaudeDesktop) NeedsBridge() bool
func (*ClaudeDesktop) PlannedEntry ¶
func (p *ClaudeDesktop) PlannedEntry(opts LinkOptions) map[string]any
PlannedEntry implements EntryPlanner for the shared mcpServers clients, including the per-client extra keys Link merges in.
type ClientInfo ¶
type ClientInfo struct {
Name string
Slug string
Detected bool
Linked bool
Transport string
ConfigPath string
// Notes carries the provisioner's post-link guidance (PostLinkNoter);
// empty for clients without client-specific caveats.
Notes []string
}
ClientInfo holds detection and link status for one client provisioner.
type ClientProvisioner ¶
type ClientProvisioner interface {
// Name returns a human-readable client name (e.g., "Claude Desktop").
Name() string
// Slug returns the CLI identifier (e.g., "claude", "cursor").
Slug() string
// Detect checks if this client is installed on the current system.
// Returns the config file path if found, empty string if not installed.
Detect() (configPath string, found bool)
// IsLinked checks if a gridctl entry already exists in the config.
IsLinked(configPath string, serverName string) (bool, error)
// Link injects or updates the gridctl entry in the client config.
Link(configPath string, opts LinkOptions) error
// Unlink removes the gridctl entry from the client config.
Unlink(configPath string, serverName string) error
// NeedsBridge returns true if this stdio-only client requires the
// mcp-remote bridge to reach the gateway's HTTP endpoint.
NeedsBridge() bool
// ListServers returns every MCP server entry present in the client
// config at configPath, gridctl-created or not. It is the read-only
// inverse of Link: a missing or empty config yields no entries and no
// error, while an unparseable one returns an error so callers can warn
// and continue scanning other clients. The config file is never written.
ListServers(configPath string) ([]ServerEntry, error)
}
ClientProvisioner handles config for a single LLM client.
type Cline ¶
type Cline struct {
// contains filtered or unexported fields
}
Cline provisions the Cline VS Code extension MCP config. Transport: stdio only (requires mcp-remote bridge). Adds Cline-specific fields: "disabled" and "alwaysAllow".
func (*Cline) Link ¶
func (p *Cline) Link(configPath string, opts LinkOptions) error
func (*Cline) ListServers ¶
func (p *Cline) ListServers(configPath string) ([]ServerEntry, error)
ListServers implements the shared read-only enumeration for standard { "mcpServers": { name: {...} } } clients.
func (*Cline) NeedsBridge ¶
func (p *Cline) NeedsBridge() bool
func (*Cline) PlannedEntry ¶
func (p *Cline) PlannedEntry(opts LinkOptions) map[string]any
PlannedEntry implements EntryPlanner for the shared mcpServers clients, including the per-client extra keys Link merges in.
type ContinueDev ¶
type ContinueDev struct {
// contains filtered or unexported fields
}
func (*ContinueDev) Detect ¶
func (c *ContinueDev) Detect() (string, bool)
func (*ContinueDev) IsLinked ¶
func (c *ContinueDev) IsLinked(configPath string, serverName string) (bool, error)
func (*ContinueDev) Link ¶
func (c *ContinueDev) Link(configPath string, opts LinkOptions) error
func (*ContinueDev) ListServers ¶
func (c *ContinueDev) ListServers(configPath string) ([]ServerEntry, error)
ListServers enumerates Continue's experimental MCP ARRAY, the one registered client that stores a list of objects (keyed by a "name" field) rather than a map.
func (*ContinueDev) Name ¶
func (c *ContinueDev) Name() string
func (*ContinueDev) NeedsBridge ¶
func (c *ContinueDev) NeedsBridge() bool
func (*ContinueDev) PlannedEntry ¶
func (c *ContinueDev) PlannedEntry(opts LinkOptions) map[string]any
func (*ContinueDev) Slug ¶
func (c *ContinueDev) Slug() string
type Cursor ¶
type Cursor struct {
// contains filtered or unexported fields
}
Cursor provisions the Cursor editor MCP config. Transport: native streamable HTTP (no bridge needed). Cursor's remote entries are url-only; the client infers the transport from the endpoint.
func (*Cursor) Link ¶
func (p *Cursor) Link(configPath string, opts LinkOptions) error
func (*Cursor) ListServers ¶
func (p *Cursor) ListServers(configPath string) ([]ServerEntry, error)
ListServers implements the shared read-only enumeration for standard { "mcpServers": { name: {...} } } clients.
func (*Cursor) NeedsBridge ¶
func (p *Cursor) NeedsBridge() bool
func (*Cursor) PlannedEntry ¶
func (p *Cursor) PlannedEntry(opts LinkOptions) map[string]any
PlannedEntry implements EntryPlanner for the shared mcpServers clients, including the per-client extra keys Link merges in.
type DetectedClient ¶
type DetectedClient struct {
Provisioner ClientProvisioner
ConfigPath string
}
DetectedClient pairs a provisioner with its found config path.
type EntryPlanner ¶
type EntryPlanner interface {
PlannedEntry(opts LinkOptions) map[string]any
}
EntryPlanner is implemented by provisioners that can report the entry value Link would write, without touching any file. The wiring ownership manager hashes the planned value to detect staleness and to adopt identical pre-existing entries silently.
type GeminiCLI ¶
type GeminiCLI struct {
// contains filtered or unexported fields
}
GeminiCLI provisions the Gemini CLI MCP config. Transport: native streamable HTTP (no bridge needed).
func (*GeminiCLI) Link ¶
func (p *GeminiCLI) Link(configPath string, opts LinkOptions) error
func (*GeminiCLI) ListServers ¶
func (p *GeminiCLI) ListServers(configPath string) ([]ServerEntry, error)
ListServers implements the shared read-only enumeration for standard { "mcpServers": { name: {...} } } clients.
func (*GeminiCLI) NeedsBridge ¶
func (p *GeminiCLI) NeedsBridge() bool
func (*GeminiCLI) PlannedEntry ¶
func (p *GeminiCLI) PlannedEntry(opts LinkOptions) map[string]any
PlannedEntry implements EntryPlanner for the shared mcpServers clients, including the per-client extra keys Link merges in.
type Goose ¶
type Goose struct {
// contains filtered or unexported fields
}
Goose provisions the Goose (Block) MCP config. Transport: native streamable HTTP (no bridge needed); Goose's extension type token is "streamable_http" (snake_case) and the URL field is "uri". Uses YAML format with "extensions" dictionary.
func (*Goose) ListServers ¶
func (g *Goose) ListServers(configPath string) ([]ServerEntry, error)
ListServers enumerates entries under Goose's YAML "extensions" key. YAML deserialization can yield map[any]any values, so entries are normalized through toStringMap before being returned.
func (*Goose) NeedsBridge ¶
func (*Goose) PlannedEntry ¶
func (g *Goose) PlannedEntry(opts LinkOptions) map[string]any
type GrokBuild ¶
type GrokBuild struct {
// contains filtered or unexported fields
}
GrokBuild provisions the Grok Build (xAI `grok` CLI) MCP config. Transport: native streamable HTTP (no bridge needed). Uses TOML format with an "mcp_servers" table keyed by server name.
func (*GrokBuild) ListServers ¶
func (g *GrokBuild) ListServers(configPath string) ([]ServerEntry, error)
ListServers enumerates entries under Grok Build's TOML "mcp_servers" table.
func (*GrokBuild) NeedsBridge ¶
func (*GrokBuild) PlannedEntry ¶
func (g *GrokBuild) PlannedEntry(opts LinkOptions) map[string]any
type ImportRead ¶
type ImportRead = OpenCodeImportRead
ImportRead is the status vocabulary for one selected config file. The name is neutral; the fields are the OpenCode read result.
func ReadProjectSource ¶
func ReadProjectSource(ctx context.Context, src ProjectSourceFile) (ImportRead, error)
ReadProjectSource reads exactly src.Path under src.ContainerKey. Statuses match ReadOpenCodeImport and carry no file contents. Cancellation is returned; a missing, unreadable, or malformed file is a status, not an error.
type LMStudio ¶
type LMStudio struct {
// contains filtered or unexported fields
}
LMStudio provisions the LM Studio desktop app MCP config (MCP host since 0.3.17). Transport: native streamable HTTP (no bridge needed). LM Studio follows Cursor's mcp.json notation and its remote entries are url-only; the in-app editor strips unknown keys such as "type", so none is written. Servers load lazily since 0.4.0, so a written entry connects on first use.
func (*LMStudio) Link ¶
func (p *LMStudio) Link(configPath string, opts LinkOptions) error
func (*LMStudio) ListServers ¶
func (p *LMStudio) ListServers(configPath string) ([]ServerEntry, error)
ListServers implements the shared read-only enumeration for standard { "mcpServers": { name: {...} } } clients.
func (*LMStudio) NeedsBridge ¶
func (p *LMStudio) NeedsBridge() bool
func (*LMStudio) PlannedEntry ¶
func (p *LMStudio) PlannedEntry(opts LinkOptions) map[string]any
PlannedEntry implements EntryPlanner for the shared mcpServers clients, including the per-client extra keys Link merges in.
func (*LMStudio) PostLinkNotes ¶
PostLinkNotes returns the client-specific guidance shown after a successful link (CLI) and on the Connections detail pane.
type LinkOptions ¶
type LinkOptions struct {
GatewayURL string // e.g., "http://localhost:8180/mcp" (streamable HTTP)
Port int // Gateway port for HTTP URL construction
ServerName string // Key name in config (default: "gridctl")
ClientID string // Stable client identifier embedded as the `client` query param (empty = none)
Group string // Tool group whose endpoint to link (empty = the default full surface)
Force bool // Overwrite existing entry
DryRun bool // Show what would change without modifying files
// OwnershipResolved marks the caller (the pkg/wiring manager) as having
// already decided ownership from the recorded lockfile state, so Link
// writes without consulting the legacy shape heuristic or the
// DeepEqual no-op check. Callers without recorded ownership leave it
// false and keep the pre-lockfile behavior.
OwnershipResolved bool
}
LinkOptions configures how a link is created.
type LinkResult ¶
type LinkResult struct {
Client string // Human-readable client name
ConfigPath string
BackupPath string
Action string // "linked", "updated", "skipped", "already-linked"
Transport string // "native HTTP" or "mcp-remote bridge"
Error error
}
LinkResult describes what happened during a link operation.
type LocalScopeEntries ¶
type LocalScopeEntries struct {
Dir string
Entries []ServerEntry
Status string
Detail string
}
LocalScopeEntries is one Claude Code local-scope match, or a file-level status when the config could not be read. Dir is empty on a file-level status. A readable file with no matching project key yields no element.
func ClaudeCodeLocalScope ¶
func ClaudeCodeLocalScope(ctx context.Context, configPath string, dirs []string) ([]LocalScopeEntries, error)
ClaudeCodeLocalScope reads configPath once and returns mcpServers entries under projects.<dir> for each directory in dirs that has that object. Path keys are compared after filepath.Clean, and case-insensitively on Windows. A missing, empty, malformed, or unreadable file yields one status element and no entries. A readable file whose projects key is absent, empty, or not an object yields no element, the same as a file with no matching directory. It does not abort the caller.
type OpenCode ¶
type OpenCode struct {
// contains filtered or unexported fields
}
OpenCode provisions the OpenCode AI coding assistant MCP config. Transport: native HTTP (no bridge needed). Uses "mcp" key with { "type": "remote", "url": "..." } entries.
func (*OpenCode) ListServers ¶
func (o *OpenCode) ListServers(configPath string) ([]ServerEntry, error)
ListServers enumerates entries under OpenCode's "mcp" key.
func (*OpenCode) NeedsBridge ¶
func (*OpenCode) PlannedEntry ¶
func (o *OpenCode) PlannedEntry(opts LinkOptions) map[string]any
type OpenCodeImportRead ¶
type OpenCodeImportRead struct {
Status string
Detail string
Entries []ServerEntry
}
OpenCodeImportRead is the result of reading exactly one selected file.
func ReadOpenCodeImport ¶
func ReadOpenCodeImport(ctx context.Context, path string) (OpenCodeImportRead, error)
ReadOpenCodeImport reads exactly path. It does not search for a sibling or a fallback file. Cancellation is checked before the read; the status is value-free and does not include file contents.
type OpenCodeImportSelection ¶
type OpenCodeImportSelection struct {
Path string
JSONPath string
JSONCPath string
Explicit bool
BothPresent bool
JSONAbsent bool
Notes []string
}
OpenCodeImportSelection is the single config file chosen for an OpenCode import. Detect's write target is a separate contract and is not changed here.
func DiscoverOpenCodeImport ¶
func DiscoverOpenCodeImport(ctx context.Context, explicit string) (OpenCodeImportSelection, error)
DiscoverOpenCodeImport chooses the OpenCode file import will read. An empty explicit path uses the Gridctl home (GRIDCTL_HOME, otherwise the OS home): existing opencode.json, or opencode.jsonc only when that JSON file is absent. It does not scan the working directory, XDG_CONFIG_HOME, OPENCODE_CONFIG, OPENCODE_CONFIG_DIR, OPENCODE_CONFIG_CONTENT, or config.json. A non-empty explicit path is returned unchanged, with no sibling lookup.
type PostLinkNoter ¶
type PostLinkNoter interface {
PostLinkNotes() []string
}
PostLinkNoter is an optional interface for provisioners with client-specific guidance worth showing after a successful link. The CLI prints the notes below the link confirmation; the web UI surfaces them on the client's Connections detail pane.
type ProjectSource ¶
ProjectSource is one project-scoped MCP file gridctl knows how to read. RelPath is relative to a project directory. ContainerKey is the object that holds server entries. Clients are the provisioner slugs that read the file. Scope is the label attached to candidates from the file.
func ProjectSources ¶
func ProjectSources() []ProjectSource
ProjectSources returns the project-scoped MCP files in scan order. opencode.jsonc precedes opencode.json so its entries win under first-occurrence keep. That order is a gridctl choice.
type ProjectSourceFile ¶
type ProjectSourceFile struct {
Dir string
Path string
Scope string
Clients []string
ContainerKey string
Depth int
}
ProjectSourceFile is one existing project file discovered by a walk. Dir is the project directory the relative path was resolved against. Depth 0 is the start directory. Path is the absolute file path.
func DiscoverProjectSources ¶
func DiscoverProjectSources(ctx context.Context, startDir string, clients []string) ([]ProjectSourceFile, error)
DiscoverProjectSources stats project files from startDir up to the nearest git root, inclusive. An empty clients filter matches every table row. A non-empty filter keeps rows whose client set intersects it, and the returned Clients slice is that intersection in table order. Missing paths are omitted. Only regular files are returned. File contents are not read.
func DiscoverProjectSourcesIn ¶
func DiscoverProjectSourcesIn(ctx context.Context, dirs []string, clients []string) ([]ProjectSourceFile, error)
DiscoverProjectSourcesIn stats project files in dirs. dirs is a nearest-first chain, typically from ProjectWalk. Depth is the index in dirs. It does not walk, does not read file contents, and does not stat paths outside dirs.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry manages all known client provisioners.
func NewRegistry ¶
func NewRegistry() *Registry
NewRegistry creates a Registry with all known client provisioners.
func NewRegistryWith ¶
func NewRegistryWith(clients ...ClientProvisioner) *Registry
NewRegistryWith creates a Registry with an explicit provisioner list. Production code uses NewRegistry; this exists so tests can drive registry consumers with fakes.
func (*Registry) AllClientInfo ¶
func (r *Registry) AllClientInfo(serverName string) []ClientInfo
AllClientInfo returns detection and link status for every registered client.
func (*Registry) DetectAll ¶
func (r *Registry) DetectAll() []DetectedClient
DetectAll returns all clients found on this system.
func (*Registry) FindBySlug ¶
func (r *Registry) FindBySlug(slug string) (ClientProvisioner, bool)
FindBySlug returns the provisioner matching the given slug.
func (*Registry) IsAnyLinked ¶
IsAnyLinked checks if any known client has a gridctl entry.
type RooCode ¶
type RooCode struct {
// contains filtered or unexported fields
}
RooCode provisions the Roo Code VS Code extension MCP config. Transport: native streamable HTTP (no bridge needed). Roo's schema requires an explicit "type" on url entries ("streamable-http" or legacy "sse"); the old "transportType" key is no longer in its schema. Adds Roo-specific fields: "disabled", "alwaysAllow".
func (*RooCode) Link ¶
func (p *RooCode) Link(configPath string, opts LinkOptions) error
func (*RooCode) ListServers ¶
func (p *RooCode) ListServers(configPath string) ([]ServerEntry, error)
ListServers implements the shared read-only enumeration for standard { "mcpServers": { name: {...} } } clients.
func (*RooCode) NeedsBridge ¶
func (p *RooCode) NeedsBridge() bool
func (*RooCode) PlannedEntry ¶
func (p *RooCode) PlannedEntry(opts LinkOptions) map[string]any
PlannedEntry implements EntryPlanner for the shared mcpServers clients, including the per-client extra keys Link merges in.
type ServerEntry ¶
ServerEntry is one raw MCP server definition found in a client config. Raw holds the entry object exactly as parsed; interpretation (command versus URL shapes, key spelling differences between clients) is the caller's concern.
type VSCode ¶
type VSCode struct {
// contains filtered or unexported fields
}
VSCode provisions the VS Code / GitHub Copilot MCP config. Transport: native streamable HTTP (no bridge needed); "type": "http" is VS Code's streamable token, with client-side fallback to SSE. Uses "servers" key (not "mcpServers") and requires a "type" field.
func (*VSCode) ListServers ¶
func (v *VSCode) ListServers(configPath string) ([]ServerEntry, error)
ListServers enumerates entries under VS Code's "servers" key.
func (*VSCode) NeedsBridge ¶
func (*VSCode) PlannedEntry ¶
func (v *VSCode) PlannedEntry(opts LinkOptions) map[string]any
PlannedEntry implementations for the clients with bespoke Link logic.
type Windsurf ¶
type Windsurf struct {
// contains filtered or unexported fields
}
Windsurf provisions the Windsurf (Codeium) editor MCP config. Transport: native streamable HTTP (no bridge needed). Entries are serverUrl-only; the client infers the transport from the endpoint.
func (*Windsurf) Link ¶
func (p *Windsurf) Link(configPath string, opts LinkOptions) error
func (*Windsurf) ListServers ¶
func (p *Windsurf) ListServers(configPath string) ([]ServerEntry, error)
ListServers implements the shared read-only enumeration for standard { "mcpServers": { name: {...} } } clients.
func (*Windsurf) NeedsBridge ¶
func (p *Windsurf) NeedsBridge() bool
func (*Windsurf) PlannedEntry ¶
func (p *Windsurf) PlannedEntry(opts LinkOptions) map[string]any
PlannedEntry implements EntryPlanner for the shared mcpServers clients, including the per-client extra keys Link merges in.
type Zed ¶
type Zed struct {
// contains filtered or unexported fields
}
Zed provisions the Zed Editor MCP config. Transport: native streamable HTTP (no bridge needed). Zed's HTTP transport is streamable-only (no legacy SSE fallback), so the entry must point at /mcp. Uses "context_servers" key (not "mcpServers").
func (*Zed) ListServers ¶
func (z *Zed) ListServers(configPath string) ([]ServerEntry, error)
ListServers enumerates entries under Zed's "context_servers" key.
func (*Zed) NeedsBridge ¶
func (*Zed) PlannedEntry ¶
func (z *Zed) PlannedEntry(opts LinkOptions) map[string]any