Toolset Functions
Toolset
Toolset(name, dsl) declares a provider-owned toolset at the top level. When declared at top level, the toolset becomes globally reusable; agents reference it via Use and services can expose it via Export.
Location: dsl/toolset.go
Context: Top-level
Purpose: Defines a provider-owned toolset (reusable across agents).
Toolsets can carry multiple tools, each with payload/result schemas, helper prompts, and metadata tags.
Example
var DocsToolset = Toolset("docs.search", func() {
ToolsetDescription("Tools for searching documentation")
Tool("search", "Search indexed documentation", func() {
Args(func() {
Attribute("query", String, "Search phrase")
Required("query")
})
Return(func() {
Attribute("documents", ArrayOf(String), "Matched snippets")
Required("documents")
})
})
})
ToolsetDescription
ToolsetDescription(description) sets a human-readable description for the current toolset. This description documents the toolset’s purpose and capabilities.
Location: dsl/toolset.go
Context: Inside Toolset
Purpose: Documents the toolset’s purpose.
Example
Toolset("data-tools", func() {
ToolsetDescription("Tools for data processing and analysis")
Tool("analyze", "Analyze dataset", func() {
// tool definition
})
})
MCPToolset
MCPToolset(service, toolset) declares an MCP-defined toolset derived from a Goa MCP server. It’s a top-level construct that agents reference via Use.
Location: dsl/toolset.go
Context: Top-level
Purpose: Declares a provider MCP suite that agents reference via Use.
There are two usage patterns:
- Goa-backed MCP server: Tool schemas are discovered from the service’s
MCPTooldeclarations - External MCP server: Tools are declared explicitly with inline schemas; requires an
mcpruntime.Callerat runtime
Example (Goa-backed)
var AssistantSuite = MCPToolset("assistant", "assistant-mcp")
var _ = Service("orchestrator", func() {
Agent("chat", func() {
Use(AssistantSuite)
})
})
Example (External with inline schemas)
var RemoteSearch = MCPToolset("remote", "search", func() {
Tool("web_search", "Search the web", func() {
Args(func() { Attribute("query", String) })
Return(func() { Attribute("results", ArrayOf(String)) })
})
})
Agent("helper", "", func() {
Use(RemoteSearch)
})
Tool
Tool(name, description, dsl) defines a callable capability inside a toolset. It has two distinct use cases:
- Inside a Toolset: Declares a tool with inline argument and return schemas
- Inside a Method: Marks a Goa service method as an MCP tool (see MCP DSL Reference)
Location: dsl/toolset.go
Context: Inside Toolset or Method
Purpose: Defines a tool (arguments, result, metadata).
Code generation emits:
- Payload/result Go structs in
tool_specs/types.go - JSON codecs (
tool_specs/codecs.go) used for activity marshaling and memory - JSON Schema definitions consumed by planners and optional validation layers
- Tool registry entries consumed by the runtime, including helper prompts and metadata
Example
Tool("search", "Search indexed documentation", func() {
ToolTitle("Document Search")
Args(func() {
Attribute("query", String, "Search phrase")
Attribute("limit", Int, "Max results", func() { Default(5) })
Required("query")
})
Return(func() {
Attribute("documents", ArrayOf(String), "Matched snippets")
Required("documents")
})
CallHintTemplate("Searching for: {{ .Query }}")
ResultHintTemplate("Found {{ len .Documents }} documents")
Tags("docs", "search")
})
Args and Return
Args(...) and Return(...) define payload/result types using the standard Goa attribute DSL. You can use:
- A function to define an inline object schema with
Attribute()calls - A Goa user type (Type, ResultType, etc.) to reuse existing type definitions
- A primitive type (String, Int, etc.) for simple single-value inputs/outputs
When using a function to define the schema inline, you can use:
Attribute(name, type, description)to define each fieldField(index, name, type, description)for positional fieldsRequired(...)to mark fields as requiredExample(...)for example valuesDefault(...)for default valuesEnum(...)for enumerated valuesMinLength/MaxLengthfor string constraintsMinimum/Maximumfor numeric constraints
Location: dsl/toolset.go
Context: Inside Tool
Purpose: Define payload/result types using the standard Goa attribute DSL.
Example
Tool("search", "Search documentation", func() {
Args(func() {
Attribute("query", String, "Search phrase")
Attribute("limit", Int, "Max results", func() {
Default(5)
Minimum(1)
Maximum(100)
})
Required("query")
})
Return(func() {
Attribute("documents", ArrayOf(String), "Matched snippets")
Attribute("count", Int, "Number of results")
Required("documents", "count")
})
})
Example (reusing types)
var SearchParams = Type("SearchParams", func() {
Attribute("query", String)
Attribute("limit", Int)
Required("query")
})
Tool("search", "Search documents", func() {
Args(SearchParams)
Return(func() {
Attribute("results", ArrayOf(String))
})
})
Sidecar
Sidecar(...) defines a typed sidecar schema for tool results. Sidecar data is attached to planner.ToolResult.Sidecar but never sent to the model provider—it’s for full-fidelity artifacts that back a bounded model-facing result.
Location: dsl/toolset.go
Context: Inside Tool
Purpose: Defines structured data attached to results but hidden from the model.
Example
Tool("get_time_series", "Get time series data", func() {
Args(func() {
Attribute("device_id", String, "Device identifier")
Required("device_id")
})
Return(func() {
Attribute("summary", String, "Summary for the model")
Attribute("count", Int, "Number of data points")
})
Sidecar(func() {
Attribute("data_points", ArrayOf(TimeSeriesPoint), "Full data")
Attribute("metadata", MapOf(String, String), "Additional metadata")
})
})
ToolTitle
ToolTitle(title) sets a human-friendly display title for the tool. If not specified, the generated code derives a title from the tool name by converting snake_case or kebab-case to Title Case.
Location: dsl/toolset.go
Context: Inside Tool
Purpose: Sets a display title for UI presentation.
Example
Tool("web_search", "Search the web", func() {
ToolTitle("Web Search")
Args(func() { /* ... */ })
})
CallHintTemplate and ResultHintTemplate
CallHintTemplate(template) and ResultHintTemplate(template) configure display templates for tool invocations and results. Templates are Go templates rendered with the tool’s payload or result to produce concise hints shown during and after execution.
Location: dsl/toolset.go
Context: Inside Tool
Purpose: Provides display hints for UIs during tool execution.
Templates are compiled with missingkey=error. Keep CallHintTemplate concise (≤140 characters recommended).
Example
Tool("search", "Search documents", func() {
Args(func() {
Attribute("query", String)
Attribute("limit", Int)
})
Return(func() {
Attribute("count", Int)
Attribute("results", ArrayOf(String))
})
CallHintTemplate("Searching for: {{ .Query }} (limit: {{ .Limit }})")
ResultHintTemplate("Found {{ .Count }} results")
})
Tags
Tags(values...) annotates tools or toolsets with metadata labels. Tags are surfaced to policy engines and telemetry, allowing you to filter or categorize tools.
Common tag patterns:
- Domain categories:
"nlp","database","api","filesystem" - Capability types:
"read","write","search","transform" - Risk levels:
"safe","destructive","external" - Performance hints:
"slow","fast","cached"
Location: dsl/toolset.go
Context: Inside Tool or Toolset
Purpose: Annotates tools/toolsets with metadata tags.
Example
Tool("delete_file", "Delete a file", func() {
Args(func() { /* ... */ })
Tags("filesystem", "write", "destructive")
})
Toolset("admin-tools", func() {
Tags("admin", "privileged")
Tool("reset_system", "Reset system state", func() {
// inherits toolset tags
})
})
BindTo
BindTo("Method") or BindTo("Service", "Method") associates a tool with a Goa service method. During evaluation, the DSL resolves the referenced method; codegen emits typed specs/codecs and transform helpers.
When a tool is bound to a method:
- The tool’s
Argsschema can differ from the method’sPayload - The tool’s
Returnschema can differ from the method’sResult - Generated adapters transform between tool and method types
- Method payload/result validation still applies
Location: dsl/toolset.go
Context: Inside Tool
Purpose: Associates a tool with a service method implementation.
Example
var _ = Service("orchestrator", func() {
Method("Search", func() {
Payload(SearchPayload)
Result(SearchResult)
})
Agent("chat", func() {
Use("docs", func() {
Tool("search", "Search documentation", func() {
Args(SearchPayload)
Return(SearchResult)
BindTo("Search") // binds to method on same service
})
})
})
})
Example (cross-service binding)
Tool("notify", "Send notification", func() {
Args(func() {
Attribute("message", String)
})
BindTo("notifications", "send") // notifications.send method
})
Inject
Inject(fields...) marks specific payload fields as “injected” (server-side infrastructure). Injected fields are:
- Hidden from the LLM (excluded from the JSON schema sent to the model)
- Exposed in the generated struct with a Setter method
- Intended to be populated by runtime hooks (
ToolInterceptor)
Location: dsl/toolset.go
Context: Inside Tool
Purpose: Marks fields as infrastructure-only, hidden from the LLM.
Example
Tool("get_data", "Get data for current session", func() {
Args(func() {
Attribute("session_id", String, "Current session ID")
Attribute("query", String, "Data query")
Required("session_id", "query")
})
Return(func() {
Attribute("data", ArrayOf(String))
})
BindTo("data_service", "get")
// session_id is required by the service but hidden from the LLM
Inject("session_id")
})
At runtime, use a ToolInterceptor to populate injected fields:
func (h *Handler) InterceptToolCall(ctx context.Context, call *planner.ToolCall) error {
if call.Name == "data.get_data" {
call.Payload.SetSessionID(ctx.Value(sessionKey).(string))
}
return nil
}
Next Steps
- Learn about Policy Functions for configuring runtime behavior
- Read the Runtime Concepts to understand how toolsets are executed