step13 · Plugins (grouping tools)
This lesson teaches how to bundle related function tools onto a Go type and attach the whole group to an agent as one slice.
What this lesson demonstrates
A “plugin” isn’t a framework construct — it’s an organizing idea. You put related tools on one type (here assistantPlugin), expose them as []tool.Tool, and hand the whole group to the agent. That keeps a domain’s tools together and lets a real plugin close over shared dependencies (a DB handle, an HTTP client). This example bundles two tools: GetWeather (takes a location) and GetCurrentTime (takes nothing), then asks one prompt that needs both.
A plugin is a type with a tools() method
There’s nothing magic here — the plugin is a plain struct whose method returns a slice of functool.MustNew tools:
func (assistantPlugin) tools() []tool.Tool {
weather := functool.MustNew(functool.Config{
Name: "GetWeather",
Description: "Gets the current weather for a location.",
}, func(_ context.Context, location string) (string, error) {
return fmt.Sprintf("The weather in %s is cloudy with a high of 15°C.", location), nil
})
now := functool.MustNew(functool.Config{
Name: "GetCurrentTime",
Description: "Gets the current time.",
}, func(context.Context, struct{}) (string, error) {
return time.Now().Format(time.RFC1123), nil
})
return []tool.Tool{weather, now}
}
newAssistant just drops assistantPlugin{}.tools() into agent.Config{Tools: ...}.
What to notice / the gotcha
- Handlers are typed; schemas are automatic. The handler’s input type drives the JSON schema the model sees —
stringforGetWeather(alocation), an emptystruct{}forGetCurrentTime(no args). Get the input type right and the model knows exactly what each tool accepts. - The plugin is stateless here, but doesn’t have to be. The pattern’s real payoff is a plugin type that holds dependencies its handler closures capture — the tools travel with their collaborators.
- One prompt, multiple tool calls. The model reads both schemas and calls
GetCurrentTime()andGetWeather("Seattle")in one turn; the framework runs the handlers in between and the model weaves both results into a single answer.
How it maps to the Agent Framework
The Go SDK has no Plugin type — and that’s the point. functool.MustNew gives you tools, agent.Config.Tools accepts any slice of them, and “plugin” is simply the convention of grouping a domain’s tools behind one Go type. It scales to as many tools as you like without new framework machinery.
Run it
go run ./tutorial/02-agents/providers/foundry/step13_plugins
The live run needs az login + FOUNDRY_PROJECT_ENDPOINT. Offline tests assert the plugin surfaces exactly GetWeather and GetCurrentTime and invoke each handler with JSON args directly — no network; the live call is gated behind AF_LIVE=1.
Key takeaways
- A “plugin” is a convention, not a framework type: put related tools on one Go type, expose them as
[]tool.Tool, and hand the whole group toagent.Config{Tools: ...}. functool.MustNewbuilds each tool; the handler’s input type drives the JSON schema the model sees —stringfor a tool with one argument, an emptystruct{}for one with none.- The pattern’s real payoff is a plugin type that holds dependencies (a DB handle, an HTTP client) which its handler closures capture, so tools travel with their collaborators.
- The model can read multiple tool schemas and call several in one turn, weaving the results into a single answer.
Further reading
- 09 · Code-Defined Skills — the next step up from grouped tools: on-demand skills defined in Go
- step14 · Code Interpreter (hosted tool) — the next lesson in this Foundry provider track
Next: step14 · Code Interpreter (hosted tool)