Handler
A handler implements a tool in your application’s language:
JavaScript with @touba/sdk or Python with touba_sdk.
| Step | JavaScript | Python |
|---|---|---|
| Install | touba sdk install javascript | touba sdk install python |
| Define the handler | defineTool | define_tool |
| Adapt business logic | run({ request, context }) | run(request, context) |
| Test and run | JavaScript tutorial | Python tutorial |
The excerpts below illustrate the JavaScript API. For a complete file connected
to your tool, select the project language and run touba prepare --mode write.
The handler is not the primary source of authority. The runtime verifies the contract before and after the call.
SDK Shape
import { defineTool } from "@touba/sdk";
await defineTool({
toolRef: "tool:module.name/ToolName",
inputSignature: [{ name: "request", type_ref: "type:module.name/Input" }],
outputType: "type:module.name/Output",
run: async ({ request, context }) => {
return await runHostCode(request, context);
},
});
toolRef identifies the expected tool. The business function receives request and context, while the SDK handles the protocol envelope.
Simple Case
import { defineTool } from "@touba/sdk";
await defineTool({
toolRef: "tool:examples.tool_simple/SearchDatasets",
inputSignature: [{ name: "request", type_ref: "type:examples.tool_simple/DatasetSearchRequest" }],
outputType: "type:examples.tool_simple/DatasetSearchResult",
requiredGrants: ["network"],
run: async ({ request }) => {
const result = await searchDatasets(request.query);
return {
title: result.title,
};
},
});
The handler performs the concrete call. The runtime remains responsible for verifying that the input matches takes and that the output matches gives.
Advanced Case
import { defineTool } from "@touba/sdk";
await defineTool({
toolRef: "tool:examples.tool_advanced/SummarizeWithContext",
inputSignature: [{ name: "request", type_ref: "type:examples.tool_advanced/SearchRequest" }],
outputType: "type:examples.tool_advanced/Summary",
requiredGrants: ["network", "secret_read", "memory_read"],
requiredSecretHandles: ["LLM_API_KEY"],
run: async ({ request, context }) => {
const apiKey = context.env.LLM_API_KEY;
const knowledge = await readProjectKnowledge(request.query);
const summary = await callLlm(apiKey, {
query: request.query,
context: knowledge,
});
return {
text: summary.text,
};
},
});
This case shows a handler that consumes a secret value injected by the runtime's minimal environment and calls a host backend.
The secret value must not be written to the output, trace, displayed prompt, or errors.
What a Handler Carries
The handler keeps non-portable details outside the .tb file:
- HTTP clients;
- provider SDKs;
- database access;
- API-specific formats;
- adapters for existing backends.
Touba keeps the contract in source and AIR; the handler keeps the real host-side effect.
Supply Chain
A handler can be attached to a supply-chain lock. The lock verifies its path, digest, command, SDK protocol, and handler_package identity. With dependency artifact extensions, it can also verify handler package files and dependency artifacts declared under that identity.
This identity helps organize handlers without scanning the workspace. Registry publication, signatures, package installation, and vulnerability auditing are governed by separate distribution mechanisms.
Runtime Contract
- The handler remains host code to audit.
- The SDK does not replace runtime checks.
- A handler authorized by the runtime still needs normal host security review.
- Dependency graph resolution, vulnerability auditing, and automatic handler package installation extend the published static registry.