Skip to Content
View as .md

Plugins

Plugins extend Squadron with additional tools. They run as separate processes, communicate over gRPC, and can be written in Go or Python. Squadron manages installation, lifecycle, and shutdown — you just declare the plugin in HCL and use its tools.

Loading Plugins

plugin "playwright" { source = "github.com/mlund01/plugin_playwright" version = "v0.0.2" settings { headless = false browser_type = "chromium" } }
AttributeTypeDescription
sourcestringPlugin source — github.com/owner/repo for a published release, or a path inside the project for a local Go or Python package
versionstringRelease tag, or "local" for local development
settingsblockPlugin-specific configuration; passed to the plugin’s Configure (optional)

On first load Squadron downloads the matching release asset from GitHub, verifies its sha256 against checksums.txt, and caches the result. Subsequent loads reuse the cache. See Distributing Plugins for the publishing side.

Local Plugin Sources

Point source at a local Go or Python package and Squadron will auto-rebuild on every config load — no manual squadron plugin build step in the dev loop:

plugin "shell" { source = "./plugin_shell" # path inside the project version = "local" }

Rules:

  • version must be "local" when source is a local path.
  • The path is resolved against the current working directory and must stay inside the project root — ../../somewhere/else is rejected at config-load time.
  • The source directory must contain a go.mod (Go plugin) or a pyproject.toml (Python plugin); Squadron auto-detects which.
  • For Go: go must be on PATH. For Python: python3 must be on PATH. Build failures surface in the config-load error with the build tool’s output attached.
  • The compiled binary or installed venv lands at the same cache location as a downloaded release: .squadron/plugins/<platform>/<name>/local/.

Build caching

Squadron hashes the source tree after each successful build and stamps the digest into runner.json (source_hash). On the next config load, if the tree still hashes the same and the entry binary is on disk, the rebuild is skipped — even for Python plugins where pip install would otherwise dominate startup. Editing any file triggers a fresh build.

The hash walk ignores VCS metadata, virtualenvs, caches, and compiled artifacts (.git/, __pycache__/, .venv/, node_modules/, *.pyc, etc.) so incidental changes don’t invalidate the cache.

Using Plugin Tools

Once loaded, plugin tools are available as plugins.<name>.<tool>:

agent "browser" { tools = [plugins.playwright.browser_navigate] } agent "browser" { tools = [plugins.playwright.all] }

Plugin Paths

Plugins are cached at .squadron/plugins/<platform>/<name>/<version>/. Each install carries a runner.json that records how to spawn the plugin:

.squadron/plugins/darwin-arm64/playwright/v0.0.2/ ├── runner.json # { "kind": "go", "entry": "plugin" } └── plugin # the Go binary
.squadron/plugins/darwin-arm64/myplug/v0.1.0/ ├── runner.json # { "kind": "python", "entry": "venv/bin/myplug" } └── venv/ # virtualenv with the plugin package installed

If runner.json is missing, Squadron falls back to executing a plugin binary in the install directory (the legacy convention).

Creating Plugins

Plugins implement four methods: Configure, Call, GetToolInfo, ListTools. Both SDKs ship a high-level decorator/generic API on top that handles schema reflection, payload validation, and result serialization for you — you just write a function.

Go

Use squadron-sdk :

package main import ( "context" "strings" squadron "github.com/mlund01/squadron-sdk" ) type EchoInput struct { Message string `json:"message" jsonschema:"required,description=The message"` AllCaps bool `json:"all_caps,omitempty"` } func main() { app := squadron.New() app.Configure(func(settings map[string]string) error { return nil }) squadron.Tool(app, "echo", "Echoes a message back", func(ctx context.Context, in EchoInput) (string, error) { if in.AllCaps { return strings.ToUpper(in.Message), nil } return in.Message, nil }) app.Serve() }

Schema is reflected from EchoInput’s jsonschema: tags. Build and install for local use:

squadron plugin build myplug ./path/to/source

The build command runs go build from the source directory and writes runner.json.

Python

Use squadron-sdk-py :

from typing import Literal from pydantic import Field from squadron_sdk import Squadron app = Squadron() @app.configure def setup(settings: dict[str, str]) -> None: app.prefix = settings.get("prefix", "") @app.tool def echo( message: str = Field(..., description="The message to echo"), all_caps: bool = False, ) -> str: """Echo a message back.""" out = message.upper() if all_caps else message return f"{app.prefix}{out}" def main(): app.serve()

Tool schemas (input and output) are reflected from your type hints by pydantic; @app.configure receives settings just like Go’s app.Configure. The plugin needs a standard pyproject.toml with one [project.scripts] entry that points at this main:

[project] name = "myplug" version = "0.1.0" dependencies = ["squadron-sdk>=0.1.1"] [project.scripts] myplug = "myplug.main:main" [build-system] requires = ["hatchling"] build-backend = "hatchling.build"

Build and install for local use:

squadron plugin build myplug ./path/to/source

The build command detects pyproject.toml, creates a virtualenv at .squadron/plugins/.../venv/, runs pip install <source>, and writes runner.json pointing at the entry script.

Splitting tools across files, including a ToolGroup from another module, and reading app state from group tools are documented in the squadron-sdk-py README .

Local Development

Two ways to iterate on a plugin:

Declarative — let HCL drive the rebuild. Point source at the plugin’s source directory and Squadron will rebuild on every config load:

plugin "myplug" { source = "./path/to/source" version = "local" }

Imperative — build once, reference by name. Run the CLI yourself, then reference the plugin without a source:

squadron plugin build myplug ./path/to/source
plugin "myplug" { version = "local" }

CLI Helpers

squadron plugin tools <name> # list tools the plugin provides squadron plugin info <name> <tool> # show input + output schemas squadron plugin call <name> <tool> '<json>' # invoke directly (useful for testing) squadron plugin build <name> <source-path> # build + install (Go or Python)
Last updated on