Snodo.Tool.Simple (snodo v0.4.1)

Copy Markdown View Source

Opt-in argument DSL for tools with straightforward object input schemas.

Snodo.Tool.Simple builds the same raw JSON Schema returned by Snodo.Tool and leaves call/2 untouched. Argument maps therefore retain protocol-native string keys, and simple tools register in Snodo.Router exactly like raw tools.

defmodule Search do
  use Snodo.Tool.Simple,
    name: "search",
    description: "Search packages",
    additional_properties: false

  argument("query", :string, required: true, min_length: 1)
  argument("page", :integer, minimum: 1)
  argument("sort", :string, enum: ["name", "downloads"])
  argument("tags", {:array, :string}, unique_items: true)

  @impl true
  def call(%{"query" => query} = arguments, _context) do
    {:ok, Snodo.Result.text("searching for #{query} on page #{arguments["page"] || 1}")}
  end
end

An argument type may be one of the JSON primitive atoms, {:array, type}, or a raw property-schema map. The :schema option merges arbitrary JSON Schema keywords into a generated property, providing a local escape hatch without abandoning the concise form. The raw Snodo.Tool DSL remains available when the input root itself needs complete hand-authored control.

An :object or {:array, :object} argument may take a do block of further argument declarations, which become the properties of that object or of each array item. output_schema/1 with a do block builds the output schema from the same declarations:

defmodule Order do
  use Snodo.Tool.Simple, name: "order", description: "Place an order"

  argument "customer", :object, required: true do
    argument "id", :string, required: true
    argument "email", :string
  end

  argument "lines", {:array, :object}, required: true, min_items: 1 do
    argument "sku", :string, required: true
    argument "quantity", :integer, required: true, minimum: 1
  end

  output_schema do
    argument "order_id", :string, required: true
    argument "total", :number, required: true
  end

  @impl true
  def call(_arguments, _context) do
    {:ok, Snodo.Result.structured(%{"order_id" => "o-1", "total" => 12.5})}
  end
end

Both compile to plain JSON Schema maps, the same ones Snodo.Tool.input_schema/1 and Snodo.Tool.output_schema/1 accept.

The :title, :icons, and :metadata options set the corresponding Snodo.Tool definition fields. They are validated when the module compiles. :wrap applies Snodo.Component.Wrap modules around call/2.

Summary

Functions

Declares one property of the tool's input schema.

Sets the tool's output schema, either from a JSON Schema map or from a do block of argument declarations.

Functions

argument(name, type, opts \\ [])

(macro)

Declares one property of the tool's input schema.

name is the property name as a string. type is a JSON type atom (:string, :integer, :number, :boolean, :array, :object, or :null), {:array, type} for an array whose "items" has that type, or a raw property-schema map.

Options:

  • :required - when true, adds name to the schema's "required" list. Defaults to false.
  • :description, :default, :enum, :pattern - set the JSON Schema keyword of the same name.
  • :min_length, :max_length, :min_items, :max_items, :minimum, :maximum, :exclusive_minimum, :exclusive_maximum, :unique_items - set the camel-case JSON Schema keyword, such as "minLength".
  • :schema - a map of other JSON Schema keywords, merged into the property last, so it overrides the generated keys.

Nested objects

When type is :object or {:array, :object}, a do block may follow. Each argument inside it declares a property of that object, or of the object in "items" for an array, and may itself take a block. :required inside the block adds the name to the nested object's "required" list. A block argument also accepts :additional_properties, a boolean or schema map set as the nested object's "additionalProperties". The other options, including :schema, apply to the property itself, so for an array they sit beside "items".

argument "filters", {:array, :object}, min_items: 1, additional_properties: false do
  argument "field", :string, required: true
  argument "value", :string
end

A block argument's :schema cannot set the keys the block generates: "properties" and "required" for :object, and "items" for {:array, :object}.

An empty or repeated name, an unknown or repeated option, an option value of the wrong type, or a block on any other type is a compile error.

output_schema(value)

(macro)

Sets the tool's output schema, either from a JSON Schema map or from a do block of argument declarations.

With a map, this is Snodo.Tool.output_schema/1:

output_schema(%{"type" => "object", "properties" => %{"version" => %{"type" => "string"}}})

With a block, the declarations take the same forms as the input schema, including nested blocks, and produce an object schema:

output_schema do
  argument "version", :string, required: true
  argument "published_at", :string
end

:additional_properties and :schema apply to the output root as the matching use Snodo.Tool.Simple options apply to the input root. They go in the same keyword list as the block:

output_schema(
  additional_properties: false,
  do:
    (
      argument("version", :string, required: true)
      argument("published_at", :string)
    )
)

With an output schema, call/2 must return structured content; see Snodo.Tool.output_schema/1. Declaring the output schema a second time when either declaration is a block, or declaring it inside an argument block, is a compile error.