Tool Catalogs & Schemas

Discover tools, schemas, and specs exported by Goa-AI agents, and learn how UIs and planners can consume them.

Why Tool Catalogs Matter

Goa-AI agents generate a single, authoritative catalog of tools from your Goa designs. This catalog powers:

  • planner tool advertisement (which tools the model can call),
  • UI discovery (tool lists, categories, schemas),
  • and external orchestrators (MCP, custom frontends) that need machine-readable specs.

You should never hand-maintain a parallel list of tools or ad-hoc JSON Schemas.

Generated Specs and tool_schemas.json

For each agent, Goa-AI emits a specs package and a JSON catalog:

  • Specs packages (gen/<service>/agents/<agent>/specs/...)

    • types.go – payload/result Go structs.
    • codecs.go – JSON codecs (encode/decode typed payloads/results).
    • specs.go[]tools.ToolSpec entries with:
      • canonical tool ID (tools.Ident),
      • payload/result schemas,
      • hints (titles, descriptions, tags).
  • JSON catalog (tool_schemas.json)

    • Location:

      gen/<service>/agents/<agent>/specs/tool_schemas.json
      
    • Contains one entry per tool with:

      • id – canonical tool ID ("<service>.<toolset>.<tool>"),
      • service, toolset, title, description, tags,
      • payload.schema and result.schema (JSON Schema).
    • Generated from the same DSL as the Go specs/codecs; if schema generation fails, goa gen fails so you never ship a drifted catalog.

This JSON file is ideal for:

  • feeding schemas to LLM providers (tool/function calling),
  • building UI forms/editors for tool payloads,
  • and offline documentation tooling.

Runtime Introspection APIs

At runtime, you do not need to read tool_schemas.json from disk. The runtime exposes an introspection API backed by the same specs:

agents   := rt.ListAgents()     // []agent.Ident
toolsets := rt.ListToolsets()   // []string

spec,   ok := rt.ToolSpec(toolID)              // single ToolSpec
schemas, ok := rt.ToolSchema(toolID)           // payload/result schemas
specs   := rt.ToolSpecsForAgent(chat.AgentID)  // []ToolSpec for one agent

Where toolID is a typed tools.Ident constant from a generated specs or agenttools package.

This is the preferred way for:

  • UIs to discover tools for a given agent,
  • orchestrators to enumerate available tools and read their schemas,
  • ops tooling to introspect tool metadata in a running system.

Choosing Between Specs and JSON

Use:

  • Specs & runtime introspection when:

    • you are inside Go code (workers, services, admin tools),
    • you want strong typing and direct access to codecs/schemas.
  • tool_schemas.json when:

    • you are outside Go (frontends, external orchestrators),
    • you want a simple static JSON catalogue to load and cache.

Both are derived from the same design; choose whichever is more convenient for your consumer.

Typed Sidecars and Artifacts

Some tools need to return rich artifacts (for example, full time series, topology graphs, or large result sets) that are useful for UIs and audits but too heavy or detailed for model providers. Goa-AI models these as typed sidecars:

  • The tool’s model-facing payload/result stay bounded and minimal.
  • An optional sidecar type carries additional data alongside the tool result; it is never sent to the model, only stored/streamed for UIs and observers.

In the specs packages, each tools.ToolSpec entry includes:

  • Payload tools.TypeSpec
  • Result tools.TypeSpec
  • Sidecar *tools.TypeSpec (optional)

tool_schemas.json mirrors this structure:

{
  "tools": [
    {
      "id": "toolset.tool",
      "service": "svc",
      "toolset": "toolset",
      "title": "Title",
      "description": "Description",
      "tags": ["tag"],
      "payload": { "name": "PayloadType", "schema": { /* ... */ } },
      "result":  { "name": "ResultType",  "schema": { /* ... */ } },
      "sidecar": { "name": "SidecarType", "schema": { /* ... */ } }
    }
  ]
}

Sidecar schemas describe the shape of planner.ToolResult.Sidecar for tools that declare a sidecar type. Goa-AI also generates:

  • a generic SidecarCodec(name string) (*tools.JSONCodec[any], bool) per toolset specs package, and

  • per-tool helpers like:

    func Get<GetTimeSeries>Sidecar(res *planner.ToolResult) (*GetTimeSeriesSidecar, error)
    func Set<GetTimeSeries>Sidecar(res *planner.ToolResult, sc *GetTimeSeriesSidecar) error
    

These helpers let executors attach typed sidecar artifacts to ToolResult instances, while UIs and analytics code can decode them using the generated schemas without guessing JSON shapes.

Agenttools and Typed Tool IDs

For exported toolsets (agent-as-tool), Goa-AI also generates an agenttools package under:

gen/<service>/agents/<agent>/agenttools/<toolset>/

These packages provide:

  • typed tool ID constants (tools.Ident),
  • alias payload/result types,
  • codecs,
  • and helper builders such as New<Search>Call(...).

Example:

import (
    chattools "example.com/assistant/gen/orchestrator/agents/chat/agenttools/search"
)

// Use a generated constant instead of ad-hoc strings
spec,   _ := rt.ToolSpec(chattools.Search)
schemas, _ := rt.ToolSchema(chattools.Search)

// Build a typed tool call
req := chattools.NewSearchCall(&chattools.SearchPayload{
    Query: "golang",
}, chattools.WithToolCallID("tc-1"))

Prefer these typed IDs and builders anywhere you reference tools (planners, orchestrators, UIs) to avoid stringly-typed bugs and keep your catalog aligned with the design.