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 ¶
- 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 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) 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) 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) 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) 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) 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) 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) Slug() string
- func (p *Cursor) Unlink(configPath string, serverName string) error
- type DetectedClient
- 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) 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) 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) Slug() string
- func (g *GrokBuild) Unlink(configPath string, serverName string) error
- type LinkOptions
- type LinkResult
- 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) Slug() string
- func (o *OpenCode) Unlink(configPath string, serverName string) error
- 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) 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) 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) 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) Slug() string
- func (z *Zed) Unlink(configPath string, serverName string) error
Constants ¶
This section is empty.
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 SSE gateway URL from a port.
func GroupGatewayHTTPURL ¶
GroupGatewayHTTPURL constructs a tool group's streamable HTTP gateway URL.
func GroupGatewayURL ¶
GroupGatewayURL constructs a tool group's SSE gateway URL from a port.
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 TransportDescription ¶
TransportDescription returns a human-readable transport description.
func TransportDescriptionFor ¶
func TransportDescriptionFor(prov ClientProvisioner) string
TransportDescriptionFor returns a transport description for a specific provisioner, distinguishing HTTP-native clients from SSE-native clients.
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
type AnythingLLM ¶
type AnythingLLM struct {
// contains filtered or unexported fields
}
AnythingLLM provisions the AnythingLLM desktop MCP config. Transport: native SSE (no bridge needed). 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
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
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
type ClientInfo ¶
type ClientInfo struct {
Name string
Slug string
Detected bool
Linked bool
Transport string
ConfigPath 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 client requires mcp-remote for SSE.
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
type ContinueDev ¶
type ContinueDev struct {
// contains filtered or unexported fields
}
ContinueDev provisions the Continue.dev extension MCP config. Transport: native SSE (no bridge needed). Config structure is different: experimental.mcpServers is an array of objects.
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.mcpServers 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) 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 SSE (no bridge needed).
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
type DetectedClient ¶
type DetectedClient struct {
Provisioner ClientProvisioner
ConfigPath string
}
DetectedClient pairs a provisioner with its found config path.
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
type Goose ¶
type Goose struct {
// contains filtered or unexported fields
}
Goose provisions the Goose (Block) MCP config. Transport: SSE (no bridge needed). 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 ¶
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 ¶
type LinkOptions ¶
type LinkOptions struct {
GatewayURL string // e.g., "http://localhost:8180/sse"
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
}
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 SSE" or "mcp-remote bridge"
Error error
}
LinkResult describes what happened during a link operation.
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 ¶
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 SSE and Streamable HTTP (no bridge needed). Adds Roo-specific fields: "transportType", "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
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 SSE (no bridge needed). 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 ¶
type Windsurf ¶
type Windsurf struct {
// contains filtered or unexported fields
}
Windsurf provisions the Windsurf (Codeium) editor MCP config. Transport: native SSE (no bridge needed).
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
type Zed ¶
type Zed struct {
// contains filtered or unexported fields
}
Zed provisions the Zed Editor MCP config. Transport: native SSE (no bridge needed). 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.