Skip to main content

Ai.ToolProvider & Ai.Tools

Examples assume aliases Ai (this module) and Js (javascript).

Ai.ToolProvider

Ai.ToolProvider is a Telo.Abstract (capability: Telo.Mount) — the single contract every source of agent tools implements. An Ai.Agent mounts providers the same way an Http.Server mounts Http.Apis, then drives them through two runtime-instance methods:

interface AiToolProviderInstance {
listTools(): Promise<ToolDescriptor[]> | ToolDescriptor[]; // { name, description?, parameters }
callTool(name: string, args: Record<string, unknown>): Promise<unknown>;
}

The agent calls listTools() to learn what to advertise to the model and callTool() to dispatch a model-requested call. It never learns which concrete provider it has — so MCP, a static list, or a future OpenAPI/registry source all compose without the agent changing.

callTool may return a plain value (JSON-stringified back to the model), a string, or multimodal content parts — a ContentPart[] ({ type: "text", text } and/or { type: "image", data, mediaType }). When a tool answers with content parts the agent carries them through the tool message untouched, so a vision tool can hand the model an image. An image part's data is raw bytes (Uint8Array, what a rasterizer/overlay tool result produces) or a base64 string; provider translation normalizes either to its wire shape.

Implementing one: declare capability: Telo.Mount, extends: Ai.ToolProvider (use Self.ToolProvider from inside @telorun/ai itself) and return an instance exposing listTools/callTool. Two ship today — Ai.Tools (below) and AiMcp.ToolProvider.

Ai.Tools

Ai.Tools is the built-in provider: a static list of tools, each wrapping any Telo.Invocable or Telo.Runnable. A Run.Sequence (a Runnable with callable inputs/outputs) can wrap a multi-step pipeline — fetch → render → annotate → return an image — as one tool the model calls.

kind: Js.Script
metadata: { name: Multiplier }
code: |
function main({ a, b }) { return { product: a * b }; }
---
kind: Ai.Tools
metadata: { name: LocalTools }
tools:
- tool: !ref Multiplier
name: multiply
description: Multiply two numbers.
parameters:
type: object
additionalProperties: false
required: [a, b]
properties: { a: { type: number }, b: { type: number } }

tools[] fields

FieldTypeRequiredPurpose
toolref (telo#Invocable | telo#Runnable)yesAny invocable or runnable — Js.Script, Http.Client.Request, Sql.Select, another Ai.Text, a Run.Sequence pipeline, …
namestringnoTool name the model sees. Defaults to the referenced resource name.
descriptionstringnoWhat the tool does (the model reads this).
parametersJSON SchemayesThe schema the model produces arguments against.
inputsCEL objectnoMaps the model's arguments into the invocable's input. Omit to forward verbatim.
resultCELnoShapes the invocable's result into the value fed back — a string, or content parts ({ type: "image", data: result.image, mediaType: result.mediaType }) to hand the model an image. Omit to JSON-stringify the output.

By default the model's arguments forward straight to invoke() and the output is JSON-stringified back to the model. The optional inputs:/result: mappings bridge invocables whose call shape differs from what the model produces:

tools:
- tool: !ref Greeter # main({ target })
name: greet
parameters:
type: object
properties: { who: { type: string } }
required: [who]
inputs:
target: "${{ arguments.who }}" # model `who` → invocable `target`
result: "${{ result.greeting }}" # shape output into a string

Inside inputs:, the arguments variable is typed from the entry's parameters; inside result:, the result variable is typed from the invocable's declared output type when it has one (otherwise open). parameters is always declared explicitly — it is not derived from the invocable's inputType, because most invocables don't declare one.

See also