BB.MCP.PeriSchema (bb_mcp v0.3.3)

Copy Markdown View Source

Converts BB command argument definitions into Peri schemas suitable for Anubis.Server.Frame.register_tool/3.

Anubis takes the Peri schema we hand it, validates incoming tool calls against it, and re-renders it as JSON Schema on the wire. So one conversion suffices for both validation and discovery.

BB types map to Peri types as follows:

:integer | :pos_integer | :non_neg_integer -> :integer
:float | :number                           -> :float
:string                                    -> :string
:boolean                                   -> :boolean
:atom                                      -> :string
{:in, values}                              -> {:enum, values}
:map | {:map, fields}                      -> :map
:keyword_list                              -> :keyword
{:list, inner}                             -> {:list, :any}
:any                                       -> :any
module / unknown                           -> :any

JSON has no atoms, so an :atom or {:in, [:a, :b]} argument is validated as a string and converted back to an atom by to_goal/2 on the way to the command. An enum of atoms only ever admits the declared members, so those convert against the declared list; a bare :atom has no declared list and falls back to String.to_existing_atom/1, leaving the client's string in place when no such atom exists rather than interning whatever was sent.

Summary

Functions

Pre-process incoming params before Peri validation.

Build a Peri field value for a single argument, applying required and default wrappers and a description meta tag where set.

Build a Peri schema (map keyed by argument name) for a command's arguments.

Translate MCP-visible command params back to the command's original BB goal.

Functions

flatten_nested_params(command, params)

@spec flatten_nested_params(BB.Dsl.Command.t(), map()) :: map()

Pre-process incoming params before Peri validation.

Some MCP clients interpret a flat property name like "target.x" as a nested path and send %{"target" => %{"x" => ...}} instead of %{"target.x" => ...}. We advertise the flat (dotted) form deliberately — nested object schemas are poorly supported by some clients — so we flatten any nested object back to the dotted form here before Peri's schema validator sees them.

Also coerces JSON whole numbers (0, 1) to floats for fields whose schema type is :float — JSON has no syntactic distinction between integer and float zero, but Peri's :float validator rejects integers.

for_argument(argument)

@spec for_argument(BB.Dsl.Command.Argument.t()) :: term()

Build a Peri field value for a single argument, applying required and default wrappers and a description meta tag where set.

for_command(command)

@spec for_command(BB.Dsl.Command.t()) :: map()

Build a Peri schema (map keyed by argument name) for a command's arguments.

to_goal(command, params)

@spec to_goal(BB.Dsl.Command.t(), map()) :: map()

Translate MCP-visible command params back to the command's original BB goal.