Arcana::Toolset
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.
-
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
-
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
In-process constructor: registers a listing on directory and
reads envelopes from a Bus mailbox.
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.
Instance methods
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.
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.