class

Arcana::Toolset

Inherits Reference < Object

A multi-tool bus provider. One directory listing, many callable tools dispatched by the tool field in the payload. Auto-registers a help tool that returns the tool manifest so callers can discover what the provider offers.

Use when you have more than one operation to expose under a single address. For a single-purpose service (echo, markdown, one chat endpoint), use Arcana::Service directly.

Two transports.

  1. In-process (Bus + Directory) — for tools registered inside the arcana daemon itself:

    ts = Arcana::Toolset.new( bus: bus, directory: dir, address: "arcana:markdown", name: "Markdown", description: "Converts markdown to HTML/ANSI", capability: "markdown", ) ts.tool("to_html", ...) { |data| ... } ts.start

  2. Over a WebSocket Client — for a separate process (your Kemal app, mj, etc.) exposing tools on the daemon's bus:

    client = Arcana::Client.new( url: "ws://localhost:19118/bus", address: "mj", name: "Minanime", description: "Image generation studio", kind: Arcana::Directory::Kind::Service, capability: "image", tags: ["image"], ) ts = Arcana::Toolset.new(client: client) ts.tool("pixelize", "Pixel-art stylize", input_schema: ...) do |data| JSON::Any.new({"image_base64" => ...}) end ts.start client.connect # blocks — WebSocket receive loop

Same tool-registration API, same manifest shape, same dispatch. The transport is the only thing that differs.

Callers (from any transport): deliver to:"mj" payload:{"tool":"help"} → {"tools":[{"name":"pixelize","description":"...","inputSchema":{...}}, ...]}

deliver to:"mj" payload:{"tool":"pixelize","prompt":"..."} → (result)

Constructors

new(bus : Bus, directory : Directory, address : String, name : String, description : String, tags : Array(String) = [] of String)

In-process constructor: registers a listing on directory and reads envelopes from a Bus mailbox.

Source
new(client : Client, name : String | Nil = nil, description : String | Nil = nil)

Client-transport constructor: wraps a Client (already configured with address/name/kind/tags). Envelopes flow over the Client's WebSocket. The daemon's Directory is populated by the Client's join frame — this constructor does not register a listing itself.

name: and description: are optional overrides for the tools manifest header; if omitted, the client's own name/description are used.

Source

Instance methods

address
Source
manifest

Return the tool manifest.

Source
start

Start listening for envelopes. On Bus transport, spawns a fiber reading from the mailbox. On Client transport, registers an on_message handler — the caller still owns Client#connect (which blocks running the WebSocket loop).

Also unions the user-provided tags with the registered tool names so arcana_directory tag:"chat" finds every entity offering a chat tool without the user having to double-declare.

Source
stop

Stop the toolset. In Bus mode, unregisters and drops the read loop. In Client mode, this just flips a flag — the caller closes the Client separately.

Source
tool(name : String, description : String, input_schema : JSON::Any | Nil = nil, &handler : JSON::Any -> JSON::Any)

Register a tool. Add all tools before calling start.

Source

Nested types