Claudex.Tool.Schema (Claudex v0.6.1)

Copy Markdown View Source

Turns a function's arguments and its @spec into JSON schema properties, in the shape the Messages API expects for a tool's input_schema.

This is pure AST-to-schema mapping with no macro magic, kept separate from Claudex.Tool so the type inference can be tested on its own. A Mod.t() reference that isn't one of the built-in types (String.t(), DateTime.t(), ...) is handed to Claudex.Tool.Schema.StructExpansion, which expands it into a nested object when Mod is a struct or an Ecto schema.

A typespec construct that can't be mapped raises Claudex.Tool.SchemaError — see build/4.

Summary

Types

Threaded through every recursive call

A function parameter: its name and whether it has a default value.

Functions

Builds the input_schema properties for a function, given its argument AST (as @on_definition receives it), the raw @spec entries accumulated on the module so far, and the compile-time env (used to resolve aliased struct references in the spec).

Extracts each parameter's name and whether it has a default value, from the argument AST alone — no @spec involved, so this never raises. Claudex.Tool uses this on its own when a tool is registered with an explicit args_schema:, since that skips @spec inspection entirely.

Types

context()

@type context() :: %{
  visited: [module()],
  env: Macro.Env.t(),
  current_module: module()
}

Threaded through every recursive call:

  • env — the compile-time environment, needed to resolve an aliased module reference (e.g. Ticket.t() after alias My.App.Ticket) into its real module name
  • visited — struct modules already being expanded on the current path, so a self- or mutually-referential struct stops instead of recursing forever
  • current_module — which module a bare t() (no module prefix) refers to right now. A struct's own @type t can reference itself as plain t(), not Mod.t() — this is how that resolves

param()

@type param() :: {name :: String.t(), has_default :: boolean()}

A function parameter: its name and whether it has a default value.

Functions

build(name, args, module_specs, env)

@spec build(atom(), [Macro.t()], [tuple()], Macro.Env.t()) :: %{
  properties: map(),
  params: [param()]
}

Builds the input_schema properties for a function, given its argument AST (as @on_definition receives it), the raw @spec entries accumulated on the module so far, and the compile-time env (used to resolve aliased struct references in the spec).

Also returns the parameter list in call order — the JSON schema alone doesn't preserve it once properties becomes a map, and Claudex.Tool needs the order later to dispatch tool calls correctly.

A parameter with no matching @spec at all gets an unconstrained (%{}) property. A parameter whose @spec type Claudex can't map — an unsupported typespec construct, or a Mod.t() that isn't a loaded struct or Ecto schema — raises Claudex.Tool.SchemaError. Fix the spec, or pass args_schema: for that tool.

params(args)

@spec params([Macro.t()]) :: [param()]

Extracts each parameter's name and whether it has a default value, from the argument AST alone — no @spec involved, so this never raises. Claudex.Tool uses this on its own when a tool is registered with an explicit args_schema:, since that skips @spec inspection entirely.