How do I set the tool call attribute on an OpenTelemetry span in JavaScript?
Rius reads the tool call id from the gen_ai.tool.call.id attribute on an execute_tool span, which you set in JavaScript with span.setAttributes from @opentelemetry/api. Set it with the tool’s name in gen_ai.tool.name and the span shows in Rius as a tool call. The tool’s arguments and result render as the span’s input and output.
How it works
A tool call in Rius is a span whose operation is execute_tool. These attributes describe it:
gen_ai.tool.call.id: the id of the model’s tool call that this execution answers. Copy it from thetool_callpart in the model’s output messages.gen_ai.tool.name: the tool’s own name, never the span name.gen_ai.tool.type:function,extension,datastoreand so on.gen_ai.operation.nameset toexecute_tool, oropeninference.span.kindset toTOOL. Set either one and Rius derives the other. Setting both is clearest.gen_ai.tool.call.argumentsandgen_ai.tool.call.result: what the tool was called with and what it returned.
Name the span the way the GenAI conventions ask, the operation followed by what it acted on, for example execute_tool get_weather. Rius does not require it, but it is what the SDKs emit, so mixed fleets read alike.
For a tool called over MCP, also set mcp.method.name to tools/call. Rius uses it to tell MCP tools from local ones when it attributes context tokens in a trace. The full attribute tables are in the span attribute reference.
Set it up
-
Register a tracer provider that exports to Rius. The vanilla OpenTelemetry SDKs page covers it in full. In Node.js, the setup is
NodeSDKwith the protobuf exporter, configured by the standard exporter variables:export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.eu.console.rius-glassflow.com" export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf" export OTEL_EXPORTER_OTLP_HEADERS="authorization=Bearer ri_..." export OTEL_SERVICE_NAME="my-agent"import { NodeSDK } from '@opentelemetry/sdk-node' import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto' const sdk = new NodeSDK({ // No url or headers in code, so the exporter reads OTEL_EXPORTER_OTLP_* traceExporter: new OTLPTraceExporter(), // Rius ingests traces only metricReaders: [], logRecordProcessors: [], }) sdk.start() // ... your agent code ... // Flushes queued spans before the process exits await sdk.shutdown() -
Start a span around each tool execution and set the tool attributes. Here
toolCallis the tool call object your own code received from the model, andgetWeatheris your own tool function:import { trace } from '@opentelemetry/api' const tracer = trace.getTracer('my-agent') const span = tracer.startSpan('execute_tool get_weather') span.setAttributes({ 'openinference.span.kind': 'TOOL', 'gen_ai.operation.name': 'execute_tool', 'gen_ai.tool.name': 'get_weather', 'gen_ai.tool.type': 'function', 'gen_ai.tool.call.id': toolCall.id, 'gen_ai.tool.call.arguments': JSON.stringify({ city: 'Paris' }), }) const result = await getWeather('Paris') span.setAttribute('gen_ai.tool.call.result', JSON.stringify(result)) span.end() -
Nest the span under your agent run the normal OpenTelemetry way, with parent context, so the tool call arrives in the same trace as the model call that requested it. If the tool fails, set
error.typeto a low-cardinality class such astool_erroron the span.
For how the exporter and attribute limit work, see Trace LLM calls with OpenTelemetry.
What you get
- The span is classified as a tool call in trace views and per-kind analytics, because
openinference.span.kindisTOOLand the operation isexecute_tool. - Rius moves
gen_ai.tool.call.argumentsandgen_ai.tool.call.resulttoinput.valueandoutput.value, so the tool’s input and output render like any other span’s. Search and filter oninput.valueandoutput.value, because the source keys are removed. - The call id links the execution to the model’s tool call it answers.
- Because the span is a tool span, it is not counted as an LLM call.
- With
mcp.method.nameset on MCP tool spans, a trace’s context attribution can report the MCP tools’ share of context tokens. - Rius computes cost on model spans from the model and token counts, so you never send a cost attribute on a tool span.
When something else fits
If you write in TypeScript or Python and want integrations that record tool calls for you, the Rius SDKs add auto-instrumentation and message normalization on top of the same attributes. Code written against raw OpenTelemetry keeps working unchanged if you adopt them later. For a language other than JavaScript, set the same keys with that language’s OpenTelemetry API.