Skip to main content
Version: 1.0.0

Create a Real Tool

This guide shows the user path for connecting a Touba tool to a handler.

1. Declare the Touba Contract​

fetch-source.tb
module examples.fetch_source

type FetchRequest
query is Text

type FetchResponse
body is Text

tool FetchSource
takes request as FetchRequest
gives FetchResponse
requires network

skill BuildSource
takes request as FetchRequest
gives FetchResponse
do
FetchSource
with request as request
into source

give source

The .tb file contains no JavaScript or Python. It declares only the contract.

2. Prepare Artifacts​

Inside a Touba project:

Terminal
touba prepare --mode plan
touba prepare --mode write
touba prepare --mode check

Expected result:

  • AIR is produced;
  • the project manifest receives the tool binding;
  • a handler skeleton is created when needed.

The manual path remains useful for understanding each step separately.

3. Compile Manually​

Terminal
touba compile fetch-source.tb --out fetch-source.air.json

4. Generate the Manifest Manually​

Terminal
touba manifest generate fetch-source.air.json --out touba.manifest.json

Review the manifest before execution. The runtime does not look for it automatically.

You can add simple payload constraints when they are part of the execution contract:

touba.manifest.json
{
"bindings": {
"tool:examples.fetch_source/FetchSource": {
"payload_constraints": {
"input": {
"request.query": {
"non_empty": true,
"max_length": 120
},
"request.mode": {
"allowed_values": ["dry_run", "review", "execute"]
}
}
}
}
}
}

The runtime verifies this constraint after the AIR schema and before the handler. The handler can keep defensive validation, but it should not be the only authority for these simple limits.

allowed_values closes a field over an explicit set of values, for example an execution mode, business status, risk level, or supported region. The list accepts only portable JSON scalars (Text, Boolean, Int, Float) so the same contract remains executable by several Touba runtimes.

5. Generate the Handler Skeleton Manually​

Terminal
touba handler stub fetch-source.air.json \
--tool-ref tool:examples.fetch_source/FetchSource \
--handler-root handlers

The skeleton refuses to overwrite an existing file by default.

6. Implement the Handler​

fetch_source.handler.mjs
import { defineTool } from "@touba/sdk";

await defineTool({
toolRef: "tool:examples.fetch_source/FetchSource",
inputSignature: [{ name: "request", type_ref: "type:examples.fetch_source/FetchRequest" }],
outputType: "type:examples.fetch_source/FetchResult",
requiredGrants: ["network"],
run: async ({ request }) => {
return {
body: `Result for ${request.query}`,
};
},
});

For a real handler, replace the body with the required call: HTTP API, database, internal service, or another host resource.

7. Run​

Terminal
touba run fetch-source.air.json \
--manifest touba.manifest.json \
--inputs-json '{"request":{"query":"touba"}}' \
--trace-out trace.json

By default, standard output displays messages produced by show. To inspect the value returned by the program directly:

Terminal
touba run fetch-source.air.json \
--manifest touba.manifest.json \
--inputs-json '{"request":{"query":"touba"}}' \
--output result

Key Points​

  • The .tb file remains the logical contract.
  • The manifest is the explicit connection.
  • The handler is bounded host code.
  • The runtime remains authoritative before dispatch for grants, secrets, scopes, payload schemas, and payload constraints.
  • show produces user output.
  • --output result displays the returned value.
  • --trace-out preserves the execution audit.