provisioner

package
v0.1.0-beta.13 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 30, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

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

This section is empty.

Variables

View Source
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.

View Source
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

func AppendClientParam(rawURL, clientID string) string

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 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

func GatewayHTTPURL(port int) string

GatewayHTTPURL constructs the streamable HTTP gateway URL from a port.

func GatewayURL

func GatewayURL(port int) string

GatewayURL constructs the SSE gateway URL from a port.

func HasComments

func HasComments(configPath string) bool

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

func TransportDescription(needsBridge bool) string

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) IsLinked

func (p *Antigravity) IsLinked(configPath string, serverName string) (bool, error)
func (p *Antigravity) Link(configPath string, opts LinkOptions) error

func (*Antigravity) Name

func (p *Antigravity) Name() string

func (*Antigravity) NeedsBridge

func (p *Antigravity) NeedsBridge() bool

func (*Antigravity) Slug

func (p *Antigravity) Slug() string
func (p *Antigravity) Unlink(configPath string, serverName string) error

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) Detect

func (p *AnythingLLM) Detect() (string, bool)

func (*AnythingLLM) IsLinked

func (p *AnythingLLM) IsLinked(configPath string, serverName string) (bool, error)
func (p *AnythingLLM) Link(configPath string, opts LinkOptions) error

func (*AnythingLLM) Name

func (p *AnythingLLM) Name() string

func (*AnythingLLM) NeedsBridge

func (p *AnythingLLM) NeedsBridge() bool

func (*AnythingLLM) Slug

func (p *AnythingLLM) Slug() string
func (p *AnythingLLM) Unlink(configPath string, serverName string) error

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.

func (*ClaudeCode) IsLinked

func (p *ClaudeCode) IsLinked(configPath string, serverName string) (bool, error)
func (p *ClaudeCode) Link(configPath string, opts LinkOptions) error

func (*ClaudeCode) Name

func (p *ClaudeCode) Name() string

func (*ClaudeCode) NeedsBridge

func (p *ClaudeCode) NeedsBridge() bool

func (*ClaudeCode) Slug

func (p *ClaudeCode) Slug() string
func (p *ClaudeCode) Unlink(configPath string, serverName string) error

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) Detect

func (p *ClaudeDesktop) Detect() (string, bool)

func (*ClaudeDesktop) IsLinked

func (p *ClaudeDesktop) IsLinked(configPath string, serverName string) (bool, error)
func (p *ClaudeDesktop) Link(configPath string, opts LinkOptions) error

func (*ClaudeDesktop) Name

func (p *ClaudeDesktop) Name() string

func (*ClaudeDesktop) NeedsBridge

func (p *ClaudeDesktop) NeedsBridge() bool

func (*ClaudeDesktop) Slug

func (p *ClaudeDesktop) Slug() string
func (p *ClaudeDesktop) Unlink(configPath string, serverName string) error

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
}

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) Detect

func (p *Cline) Detect() (string, bool)

func (*Cline) IsLinked

func (p *Cline) IsLinked(configPath string, serverName string) (bool, error)
func (p *Cline) Link(configPath string, opts LinkOptions) error

func (*Cline) Name

func (p *Cline) Name() string

func (*Cline) NeedsBridge

func (p *Cline) NeedsBridge() bool

func (*Cline) Slug

func (p *Cline) Slug() string
func (p *Cline) Unlink(configPath string, serverName string) error

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 (c *ContinueDev) Link(configPath string, opts LinkOptions) error

func (*ContinueDev) Name

func (c *ContinueDev) Name() string

func (*ContinueDev) NeedsBridge

func (c *ContinueDev) NeedsBridge() bool

func (*ContinueDev) Slug

func (c *ContinueDev) Slug() string
func (c *ContinueDev) Unlink(configPath string, serverName string) error

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) Detect

func (p *Cursor) Detect() (string, bool)

func (*Cursor) IsLinked

func (p *Cursor) IsLinked(configPath string, serverName string) (bool, error)
func (p *Cursor) Link(configPath string, opts LinkOptions) error

func (*Cursor) Name

func (p *Cursor) Name() string

func (*Cursor) NeedsBridge

func (p *Cursor) NeedsBridge() bool

func (*Cursor) Slug

func (p *Cursor) Slug() string
func (p *Cursor) Unlink(configPath string, serverName string) error

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) Detect

func (p *GeminiCLI) Detect() (string, bool)

func (*GeminiCLI) IsLinked

func (p *GeminiCLI) IsLinked(configPath string, serverName string) (bool, error)
func (p *GeminiCLI) Link(configPath string, opts LinkOptions) error

func (*GeminiCLI) Name

func (p *GeminiCLI) Name() string

func (*GeminiCLI) NeedsBridge

func (p *GeminiCLI) NeedsBridge() bool

func (*GeminiCLI) Slug

func (p *GeminiCLI) Slug() string
func (p *GeminiCLI) Unlink(configPath string, serverName string) error

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) Detect

func (g *Goose) Detect() (string, bool)

func (*Goose) IsLinked

func (g *Goose) IsLinked(configPath string, serverName string) (bool, error)
func (g *Goose) Link(configPath string, opts LinkOptions) error

func (*Goose) Name

func (g *Goose) Name() string

func (*Goose) NeedsBridge

func (g *Goose) NeedsBridge() bool

func (*Goose) Slug

func (g *Goose) Slug() string
func (g *Goose) Unlink(configPath string, serverName string) error

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) Detect

func (g *GrokBuild) Detect() (string, bool)

func (*GrokBuild) IsLinked

func (g *GrokBuild) IsLinked(configPath string, serverName string) (bool, error)
func (g *GrokBuild) Link(configPath string, opts LinkOptions) error

func (*GrokBuild) Name

func (g *GrokBuild) Name() string

func (*GrokBuild) NeedsBridge

func (g *GrokBuild) NeedsBridge() bool

func (*GrokBuild) Slug

func (g *GrokBuild) Slug() string
func (g *GrokBuild) Unlink(configPath string, serverName string) error

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)
	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) Detect

func (o *OpenCode) Detect() (string, bool)

func (*OpenCode) IsLinked

func (o *OpenCode) IsLinked(configPath string, serverName string) (bool, error)
func (o *OpenCode) Link(configPath string, opts LinkOptions) error

func (*OpenCode) Name

func (o *OpenCode) Name() string

func (*OpenCode) NeedsBridge

func (o *OpenCode) NeedsBridge() bool

func (*OpenCode) Slug

func (o *OpenCode) Slug() string
func (o *OpenCode) Unlink(configPath string, serverName string) error

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 (*Registry) AllClientInfo

func (r *Registry) AllClientInfo(serverName string) []ClientInfo

AllClientInfo returns detection and link status for every registered client.

func (*Registry) AllSlugs

func (r *Registry) AllSlugs() []string

AllSlugs returns the slugs of all registered clients.

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

func (r *Registry) IsAnyLinked(serverName string) bool

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) Detect

func (p *RooCode) Detect() (string, bool)

func (*RooCode) IsLinked

func (p *RooCode) IsLinked(configPath string, serverName string) (bool, error)
func (p *RooCode) Link(configPath string, opts LinkOptions) error

func (*RooCode) Name

func (p *RooCode) Name() string

func (*RooCode) NeedsBridge

func (p *RooCode) NeedsBridge() bool

func (*RooCode) Slug

func (p *RooCode) Slug() string
func (p *RooCode) Unlink(configPath string, serverName string) error

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) Detect

func (v *VSCode) Detect() (string, bool)

func (*VSCode) IsLinked

func (v *VSCode) IsLinked(configPath string, serverName string) (bool, error)
func (v *VSCode) Link(configPath string, opts LinkOptions) error

func (*VSCode) Name

func (v *VSCode) Name() string

func (*VSCode) NeedsBridge

func (v *VSCode) NeedsBridge() bool

func (*VSCode) Slug

func (v *VSCode) Slug() string
func (v *VSCode) Unlink(configPath string, serverName string) error

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) Detect

func (p *Windsurf) Detect() (string, bool)

func (*Windsurf) IsLinked

func (p *Windsurf) IsLinked(configPath string, serverName string) (bool, error)
func (p *Windsurf) Link(configPath string, opts LinkOptions) error

func (*Windsurf) Name

func (p *Windsurf) Name() string

func (*Windsurf) NeedsBridge

func (p *Windsurf) NeedsBridge() bool

func (*Windsurf) Slug

func (p *Windsurf) Slug() string
func (p *Windsurf) Unlink(configPath string, serverName string) error

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) Detect

func (z *Zed) Detect() (string, bool)

func (*Zed) IsLinked

func (z *Zed) IsLinked(configPath string, serverName string) (bool, error)
func (z *Zed) Link(configPath string, opts LinkOptions) error

func (*Zed) Name

func (z *Zed) Name() string

func (*Zed) NeedsBridge

func (z *Zed) NeedsBridge() bool

func (*Zed) Slug

func (z *Zed) Slug() string
func (z *Zed) Unlink(configPath string, serverName string) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL