Create a Real Tool
This guide shows the user path for connecting a Touba tool to a handler.
1. Declare the Touba Contract
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:
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
touba compile fetch-source.tb --out fetch-source.air.json
4. Generate the Manifest Manually
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:
{
"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
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
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
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:
touba run fetch-source.air.json \
--manifest touba.manifest.json \
--inputs-json '{"request":{"query":"touba"}}' \
--output result
Key Points
- The
.tbfile 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.
showproduces user output.--output resultdisplays the returned value.--trace-outpreserves the execution audit.