Documentation
¶
Overview ¶
Package installsource implements the `install_source` tool: a two-phase installer for fairpeer skills and MCP servers. A single call resolves a source (URL, local file/folder, .mcp.json, package name, or local executable) into a deterministic plan. When the caller sets apply=true, any registered ApprovalFunc may still deny that exact plan before writes or MCP connects run.
The two-phase design exists so the model (or a UI) can inspect a plan before it touches disk or spawns subprocesses. `install_source` deliberately does not run a README's `curl | sh` chain: it locates a concrete manifest (SKILL.md / <name>.md / <name>/SKILL.md / nested skill roots / .mcp.json / mcpServers entry) and describes what it would do, and only then does it act on apply=true. Single skills are written to the canonical <name>/SKILL.md layout; flat <name>.md is treated as compatibility input.
Concurrency: each Execute call is independent; the tool does not lock the filesystem. Callers that want to serialize installs (e.g. two parallel calls for the same skill name) should do so in the host.
Package installsource: install_source.go is the tool entrypoint. It defines the public Options/Execute surface, the JSON Schema, and the end-to-end pipeline that turns a request into a plan and (optionally) into a series of apply calls.
Index ¶
- Variables
- func InstalledSkillNames(homeDir string) map[string]string
- func NewMarketTool(installer *installSourceTool) tool.Tool
- func NewMarketToolFromOptions(opts Options) tool.Tool
- func NewTool(opts Options) tool.Tool
- type ApprovalFunc
- type CatalogEntry
- type MCPConnectResult
- type MCPConnector
- type Manifest
- type ManifestEntry
- type MarketSource
- type MarketSourceMeta
- type OnDisconnectFunc
- type Options
- type RiskLevel
- type SafetyFinding
- type SkillMarketTool
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrAuthRequired: the upstream demanded credentials that the request // did not carry. Surface a hint to set the relevant env var or header. ErrAuthRequired = errors.New("install_source: authentication required") // ErrBinaryMissing: a stdio MCP server references a command that is not // on PATH or not present at the given path. ErrBinaryMissing = errors.New("install_source: command or runtime not found") // ErrAlreadyExists: a target file / config entry already exists and the // call did not opt into replace=true. ErrAlreadyExists = errors.New("install_source: target already exists") // ErrUnsafeLinkTarget: a link-mode skill install would create a symlink // that escapes the expected skill roots — typically an attempt to // read arbitrary host files. ErrUnsafeLinkTarget = errors.New("install_source: link target escapes skill roots") // ErrSourceUnreadable: a URL did not respond, returned non-2xx, or a // local path was not readable. ErrSourceUnreadable = errors.New("install_source: source is not readable") // ErrManifestMissing: a path was reachable but contained no installable // artifact (no SKILL.md, no .mcp.json, no executable, etc.). ErrManifestMissing = errors.New("install_source: no installable manifest") // ErrInvalidManifest: a manifest existed but did not validate (missing // required fields, unknown transport, etc.). ErrInvalidManifest = errors.New("install_source: manifest did not validate") // ErrUnsupportedKind: kind was set explicitly to something the resolver // cannot satisfy (e.g. kind=skill for a remote MCP endpoint). ErrUnsupportedKind = errors.New("install_source: kind does not match source") // ErrApprovalDenied: the host's ApprovalFunc returned a non-nil error, // or the call set apply=true while the host requires explicit consent. ErrApprovalDenied = errors.New("install_source: host denied the install") )
Sentinel errors. Callers use errors.Is to map a failure to a remediation hint without scraping error messages.
Functions ¶
func InstalledSkillNames ¶
InstalledSkillNames reads the install manifest from both project and global scopes and returns a map of skill-name → source-URL. Used by the frontend to mark skills as "already installed" in search results.
func NewMarketTool ¶
NewMarketTool returns a skill_market tool wired to an existing installSourceTool (sharing the same httpClient, roots, and MCP connector for installs). The caller passes the *installSourceTool obtained from NewTool via type assertion.
func NewMarketToolFromOptions ¶
NewMarketToolFromOptions constructs both the installer and the market tool from Options, for boot wiring where the caller doesn't hold a *installSourceTool. The installer is built with the same SSRF-guarded httpClient as NewTool.
func NewTool ¶
NewTool returns a tool.Tool that callers register with the agent's Registry. The returned tool is safe to call from any goroutine; the underlying config/config.SaveTo paths do their own per-file locking.
Example ¶
ExampleNewTool is a godoc example that exercises the public surface without touching the filesystem. It also serves as smoke coverage that the Schema() output is valid JSON and the tool does not panic on a well-formed call.
tl := NewTool(Options{
ProjectRoot: "/tmp/example",
HomeDir: "/tmp/example-home",
})
raw, _ := json.Marshal(map[string]any{"source": "https://example.com/mcp"})
out, _ := tl.Execute(context.Background(), raw)
var resp response
_ = json.Unmarshal([]byte(out), &resp)
fmt.Printf("status=%s kind=%s skill=%d mcp=%d\n",
resp.Status, resp.Kind, resp.Kinds.Skill, resp.Kinds.MCP)
Output: status=planned kind=mcp skill=0 mcp=1
Types ¶
type ApprovalFunc ¶
type ApprovalFunc func(actions []action) error
ApprovalFunc is invoked between plan and apply when apply=true. Return nil to allow the install, or a non-nil error to refuse it. The action list reflects the exact set the apply step is about to perform; a host (e.g. the desktop TUI) can show it to the user and decide synchronously.
type CatalogEntry ¶
type CatalogEntry struct {
Source string `json:"source"` // source ID
Name string `json:"name"` // skill name (install name)
Slug string `json:"slug"` // source-native identifier (clawhub slug)
Author string `json:"author,omitempty"` // author handle (clawhub ownerHandle)
Description string `json:"description"` // one-line summary
Topics []string `json:"topics"` // categories/tags
Installs int `json:"installs"` // download count (clawhub has, GitHub doesn't)
ContentURL string `json:"contentUrl"` // URL to fetch SKILL.md (install path)
InstallRef string `json:"installRef"` // value to pass to install_source's `source`
}
CatalogEntry is one installable skill from a market source, in a unified cross-source format. This is what browse/search returns and what the agent presents to the user.
func BuiltinCatalog ¶
func BuiltinCatalog() []CatalogEntry
BuiltinCatalog returns the offline curated skill catalog (exported for the desktop settings panel to show without network).
func BuiltinMcpCatalog ¶
func BuiltinMcpCatalog() []CatalogEntry
BuiltinMcpCatalog returns the compiled curated list of MCP servers.
type MCPConnectResult ¶
type MCPConnectResult struct {
ToolCount int
Disconnect func() // optional; nil means rollback is not possible
}
MCPConnectResult is what the ConnectMCP callback returns. Disconnect is optional; when non-nil, the apply step will call it to undo a connect whose persistence (SaveTo) failed — closing the "ghost install" window.
type MCPConnector ¶
type MCPConnector func(config.PluginEntry) (MCPConnectResult, error)
MCPConnector is the host-provided hook that turns a PluginEntry into a live MCP connection. The returned Disconnect, if any, is used by the install_source tool to roll back a failed persistence step.
type Manifest ¶
type Manifest struct {
Skills map[string]ManifestEntry `json:"skills"`
}
Manifest is the on-disk map of skill-name → provenance. Loaded/saved per scope (project skills-root vs global skills-root).
type ManifestEntry ¶
type ManifestEntry struct {
Source string `json:"source"` // URL or local path the skill came from
ContentHash string `json:"content_hash"` // sha256 of the installed SKILL.md content
InstalledAt time.Time `json:"installed_at"` // when install_source wrote it
Scope string `json:"scope"` // "project" | "global"
Mode string `json:"mode"` // "copy" | "link"
}
ManifestEntry is one installed skill's provenance record.
type MarketSource ¶
type MarketSource struct {
ID string `json:"id"` // "anthropics" / "openai" / "clawhub" / "builtin"
Name string `json:"name"` // human label
Type string `json:"type"` // "github-repo" / "clawhub-api" / "builtin-catalog"
URL string `json:"url"` // repo URL / API base / empty for builtin
}
MarketSource describes one catalog source the user can browse/search.
func DefaultMarketSources ¶
func DefaultMarketSources() []MarketSource
DefaultMarketSources returns the built-in default sources, available with no user configuration. Multiple sources avoid depending on a single service: if clawhub is down, GitHub repos still work; if GitHub rate-limits, the builtin catalog is offline-available.
type MarketSourceMeta ¶
type MarketSourceMeta struct {
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
}
MarketSourceMeta is the desktop-facing view of a market source (no internal types leaked).
func DefaultMarketSourceMetas ¶
func DefaultMarketSourceMetas() []MarketSourceMeta
DefaultMarketSourceMetas returns the default sources as lightweight metadata for the frontend.
type OnDisconnectFunc ¶
OnDisconnectFunc tells the host to remove a server from the live session and drop the corresponding mcp__<name>__ tools from its Registry. It returns true when a live server was actually removed, letting replace/rollback restore the old connection only when there was one.
type Options ¶
type Options struct {
ProjectRoot string
HomeDir string
HTTPClient *http.Client
ConnectMCP MCPConnector
OnDisconnect OnDisconnectFunc
Approval ApprovalFunc
}
Options configure the install_source tool. ProjectRoot "" and HomeDir "" fall back to os.Getwd / os.UserHomeDir at construction time.
type RiskLevel ¶
type RiskLevel string
RiskLevel classifies how dangerous an action is. The install-capability skill prompt tells the model to call apply=true only when every action is low or medium, or to ask the user first when any action is high.
const ( // RiskLow is read-mostly safe: copy/link of a single skill file, or // connecting an MCP endpoint whose URL the user already trusts. RiskLow RiskLevel = "low" // RiskMedium is a write that mutates the active config (new MCP server, // new skill registered into a project root the user already shares). RiskMedium RiskLevel = "medium" // RiskHigh is a write the user almost certainly wants to see first: a // symlink target outside any expected scope, a remote URL with auth // headers, a package name that triggers an out-of-process fetch, or a // replace of an existing MCP server. RiskHigh RiskLevel = "high" )
type SafetyFinding ¶
type SafetyFinding struct {
Level string // "info" | "warn" | "block"
Code string // a stable identifier for the pattern family
Description string // human-readable explanation shown in riskReasons
}
SafetyFinding is one detected concern in a skill body.
type SkillMarketTool ¶
type SkillMarketTool struct {
// contains filtered or unexported fields
}
SkillMarketTool is the marketplace browse/search/install tool.
func (SkillMarketTool) Description ¶
func (SkillMarketTool) Description() string
func (*SkillMarketTool) Execute ¶
func (m *SkillMarketTool) Execute(ctx context.Context, args json.RawMessage) (string, error)
func (SkillMarketTool) Name ¶
func (SkillMarketTool) Name() string
func (SkillMarketTool) ReadOnly ¶
func (SkillMarketTool) ReadOnly() bool
func (SkillMarketTool) Schema ¶
func (SkillMarketTool) Schema() json.RawMessage