installsource

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Aug 6, 2026 License: MIT Imports: 24 Imported by: 0

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

Examples

Constants

This section is empty.

Variables

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

func InstalledSkillNames(homeDir string) map[string]string

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

func NewMarketTool(installer *installSourceTool) tool.Tool

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

func NewMarketToolFromOptions(opts Options) tool.Tool

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

func NewTool(opts Options) tool.Tool

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

type OnDisconnectFunc func(serverName string) bool

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

Jump to

Keyboard shortcuts

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