Snodo.Tool.Simple (snodo v0.4.0)

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.

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.