LLM wrappers capture tools the model executes inside a wrapped call. For tools your own code invokes directly, wrap the function with wrapTool — every execution is then captured as a tool event with input, output, duration, and status.
Wrap a function
import { Carbon } from "@carbon-js/sdk";
const carbon = new Carbon();
const getWeather = carbon.wrapTool({
name: "get_weather",
run: async ({ city }: { city: string }) => {
const response = await fetch(`https://api.weather.example/v1/${city}`);
return response.json();
},
});
const weather = await getWeather({ city: "Tokyo" });
The wrapped function has the same signature and return type as run. Sync and async functions both work.
- On success, the event records the serialized input and output with
status.state: "ok".
- On failure, the event records the error with
status.state: "error" and the original error is rethrown — wrapping never swallows exceptions.
The wrapped function accepts one optional trailing argument carrying Carbon metadata. It is detected by the presence of a traceId, context, or additionalProperties key and is not passed to your run function:
const traceId = carbon.createTraceId();
const weather = await getWeather(
{ city: "Tokyo" },
{ traceId, context: { agentId: "travel-agent", userId: "user-481" } },
);
This puts the tool event on the same trace as the LLM calls around it — see Traces and context.
Detection is by key presence only. If your tool’s real last argument has a
traceId, context, or additionalProperties key, it would be treated as
Carbon metadata — rename the field or wrap the value in an object.